openMSX
imgui.cc
Go to the documentation of this file.
1// dear imgui, v1.90.8 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/7503 (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)
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
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 ImGuiConfigFlags_NavEnableSetMousePos flag.
188 Enabling ImGuiConfigFlags_NavEnableSetMousePos + 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/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
442 - old: CaptureKeyboardFromApp(bool)
443 - new: SetNextFrameWantCaptureKeyboard(bool)
444 - old: CaptureMouseFromApp(bool)
445 - new: SetNextFrameWantCaptureMouse(bool)
446 - 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).
447 - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
448 - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
449 - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
450 - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
451 - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
452 - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
453 - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
454 - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
455 - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
456 for various reasons those changes makes sense. They are being made because making some of those API public.
457 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.
458 - 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)
459 - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
460 - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...);
461 - 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.
462 - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
463 - 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)
464 - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
465 - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
466 - 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).
467 - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
468 with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
469 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.
470 (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
471 - 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)
472 - 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)
473 - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
474 - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
475 - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
476 - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
477 - 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.
478 - 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.
479 - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
480 - 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).
481 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.
482 - 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.
483 - old: BeginChild("Name", size, true)
484 - new: BeginChild("Name", size, ImGuiChildFlags_Border)
485 - old: BeginChild("Name", size, false)
486 - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
487 - 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.
488 - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
489 - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
490 - 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).
491 - 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)
492 - 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).
493 - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
494 - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
495 - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
496 - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
497 - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
498 - GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
499 - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
500 - 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
501 - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
502 - 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)
503 - 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).
504 - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
505 - 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.
506 - 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)
507 - 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.
508 - 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.
509 - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
510 - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
511 - ListBoxFooter() -> use EndListBox()
512 - 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().
513 - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
514 - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
515 - ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp
516 - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
517 - ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic()
518 - ImDrawList::PathBezierCurveTo() -> use ImDrawList::PathBezierCubicCurveTo()
519 - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
520 - 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.
521 - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
522 Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
523 it has been frequently requested by people to use our own. We had an opt-in define which was
524 previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
525 - OK: #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
526 - Error: #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
527 - 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.
528 - 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
529 - 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.
530 - 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
531 - ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl
532 - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
533 - ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt
534 - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
535 the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
536 the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
537 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.
538 - 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
539 this will require uses of legacy backend-dependent indices to be casted, e.g.
540 - with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
541 - with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A')
542 - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
543 - 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);
544 - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
545 - 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.
546 - 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.
547 - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
548 - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
549 this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
550 - previously this would make the window content size ~200x200:
551 Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
552 - instead, please submit an item:
553 Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
554 - alternative:
555 Begin(...) + Dummy(ImVec2(200,200)) + End();
556 - content size is now only extended when submitting an item!
557 - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
558 - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
559 - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
560 - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
561 - 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));
562 - 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.
563 - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
564 - 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));
565 - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
566 - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
567 - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
568 - Official backends from 1.87+ -> no issue.
569 - Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
570 - Custom backends not writing to io.NavInputs[] -> no issue.
571 - Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
572 - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
573 - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
574 - 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.
575 - 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.
576 - 2022/01/20 (1.87) - inputs: reworded gamepad IO.
577 - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
578 - 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).
579 - 2022/01/17 (1.87) - inputs: reworked mouse IO.
580 - Backend writing to io.MousePos -> backend should call io.AddMousePosEvent()
581 - Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent()
582 - Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent()
583 - Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
584 note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
585 read https://github.com/ocornut/imgui/issues/4921 for details.
586 - 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.
587 - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
588 - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
589 - 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).
590 - 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.*
591 - 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.
592 - 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.
593 - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
594 - 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'.
595 - 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)
596 - ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen()
597 - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
598 - ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
599 - ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect
600 - ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
601 - 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
602 - 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.
603 - 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.
604 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
605 - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
606 - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
607 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
608 - if you are using official backends from the source tree: you have nothing to do.
609 - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
610 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
611 - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
612 - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
613 - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
614 flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
615 breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
616 - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
617 - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
618 - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
619 - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
620 this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
621 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.
622 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).
623 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
624 - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
625 - 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.
626 - 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.
627 - 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'.
628 - 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.
629 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
630 - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
631 - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
632 - 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.
633 - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
634 - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
635 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
636 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
637 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
638 - ImGuiInputTextCallback -> use ImGuiTextEditCallback
639 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
640 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
641 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
642 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
643 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
644 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
645 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
646 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
647 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
648 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
649 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
650 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
651 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
652 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
653 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
654 - 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).
655 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
656 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
657 - 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.
658 - 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.
659 - 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!
660 - 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().
661 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
662 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
663 - if you omitted the 'power' parameter (likely!), you are not affected.
664 - 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.
665 - 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.
666 see https://github.com/ocornut/imgui/issues/3361 for all details.
667 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.
668 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
669 - 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.
670 - 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.
671 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
672 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
673 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
674 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
675 - 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.
676 - 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.
677 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
678 - ShowTestWindow() -> use ShowDemoWindow()
679 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
680 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
681 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
682 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
683 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
684 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
685 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
686 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
687 - 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.
688 - 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).
689 - 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.
690 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
691 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
692 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
693 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
694 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
695 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
696 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
697 - ImFont::Glyph -> use ImFontGlyph
698 - 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.
699 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.
700 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).
701 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
702 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
703 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
704 - 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.
705 - 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
706 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.
707 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.
708 Please reach out if you are affected.
709 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
710 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
711 - 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.
712 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
713 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
714 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
715 - 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!
716 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
717 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
718 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
719 - 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.
720 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
721 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
722 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
723 - 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.
724 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
725 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
726 - 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.
727 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
728 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
729 - 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).
730 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
731 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
732 - 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.
733 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
734 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
735 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
736 - 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.).
737 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
738 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
739 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
740 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
741 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
742 - 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.
743 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
744 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.
745 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.
746 - 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",
747 consistent with other functions. Kept redirection functions (will obsolete).
748 - 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.
749 - 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).
750 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
751 - 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.
752 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
753 - 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.
754 - 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.
755 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
756 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
757 - removed Shutdown() function, as DestroyContext() serve this purpose.
758 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
759 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
760 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
761 - 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.
762 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
763 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
764 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
765 - 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.
766 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
767 - 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
768 - 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.
769 - 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.
770 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
771 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
772 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
773 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
774 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
775 - 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.
776 - 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.
777 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.
778 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
779 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
780 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
781 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
782 - 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.
783 - 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.
784 - 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.
785 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
786 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
787 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
788 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
789 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
790 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
791 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
792 - 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).
793 - 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)".
794 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
795 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
796 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
797 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
798 - 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.
799 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
800 - 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.
801 - 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).
802 - 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).
803 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
804 - 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.
805 - 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.
806 - 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))'
807 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
808 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
809 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
810 - 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().
811 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
812 - 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.
813 - 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.
814 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
815 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.
816 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:
817 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); }
818 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.
819 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
820 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
821 - 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).
822 - 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.
823 - 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).
824 - 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)
825 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
826 - 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.
827 - 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.
828 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
829 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
830 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
831 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.
832 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!
833 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
834 - 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.
835 - 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
836 - 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.
837 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
838 - 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.
839 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
840 - 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.
841 - the signature of the io.RenderDrawListsFn handler has changed!
842 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
843 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
844 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
845 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
846 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
847 - 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.
848 - 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!
849 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
850 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
851 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
852 - 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.
853 - 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
854 - 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!
855 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
856 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
857 - 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.
858 - 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.
859 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
860 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
861 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
862 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
863 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
864 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
865 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
866 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
867 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
868 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
869 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
870 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
871 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
872 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
873 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
874 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
875 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
876 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
877 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
878 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
879 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.
880 - 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()
881 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
882 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
883 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
884 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
885 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
886 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
887 - 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)
888 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
889 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
890 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
891 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
892 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
893 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
894
895
896 FREQUENTLY ASKED QUESTIONS (FAQ)
897 ================================
898
899 Read all answers online:
900 https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
901 Read all answers locally (with a text editor or ideally a Markdown viewer):
902 docs/FAQ.md
903 Some answers are copied down here to facilitate searching in code.
904
905 Q&A: Basics
906 ===========
907
908 Q: Where is the documentation?
909 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
910 - Run the examples/ applications and explore them.
911 - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
912 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
913 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
914 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
915 - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
916 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
917 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
918 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
919 - Your programming IDE is your friend, find the type or function declaration to find comments
920 associated with it.
921
922 Q: What is this library called?
923 Q: Which version should I get?
924 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
925 >> See https://www.dearimgui.com/faq for details.
926
927 Q&A: Integration
928 ================
929
930 Q: How to get started?
931 A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
932
933 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
934 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
935 >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
936
937 Q. How can I enable keyboard or gamepad controls?
938 Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
939 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
940 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
941 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
942 >> See https://www.dearimgui.com/faq
943
944 Q&A: Usage
945 ----------
946
947 Q: About the ID Stack system..
948 - Why is my widget not reacting when I click on it?
949 - How can I have widgets with an empty label?
950 - How can I have multiple widgets with the same label?
951 - How can I have multiple windows with the same label?
952 Q: How can I display an image? What is ImTextureID, how does it work?
953 Q: How can I use my own math types instead of ImVec2?
954 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
955 Q: How can I display custom shapes? (using low-level ImDrawList API)
956 >> See https://www.dearimgui.com/faq
957
958 Q&A: Fonts, Text
959 ================
960
961 Q: How should I handle DPI in my application?
962 Q: How can I load a different font than the default?
963 Q: How can I easily use icons in my application?
964 Q: How can I load multiple fonts?
965 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
966 >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
967
968 Q&A: Concerns
969 =============
970
971 Q: Who uses Dear ImGui?
972 Q: Can you create elaborate/serious tools with Dear ImGui?
973 Q: Can you reskin the look of Dear ImGui?
974 Q: Why using C++ (as opposed to C)?
975 >> See https://www.dearimgui.com/faq
976
977 Q&A: Community
978 ==============
979
980 Q: How can I help?
981 A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
982 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
983 This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
984 >>> See https://github.com/ocornut/imgui/wiki/Funding
985 - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
986 - 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!
987 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
988 You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
989 But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
990 - 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).
991
992*/
993
994//-------------------------------------------------------------------------
995// [SECTION] INCLUDES
996//-------------------------------------------------------------------------
997
998#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
999#define _CRT_SECURE_NO_WARNINGS
1000#endif
1001
1002#ifndef IMGUI_DEFINE_MATH_OPERATORS
1003#define IMGUI_DEFINE_MATH_OPERATORS
1004#endif
1005
1006#include "imgui.h"
1007#ifndef IMGUI_DISABLE
1008#include "imgui_internal.h"
1009
1010// System includes
1011#include <stdio.h> // vsnprintf, sscanf, printf
1012#include <stdint.h> // intptr_t
1013
1014// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1015#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1016#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1017#endif
1018
1019// [Windows] OS specific includes (optional)
1020#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1021#define IMGUI_DISABLE_WIN32_FUNCTIONS
1022#endif
1023#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1024#ifndef WIN32_LEAN_AND_MEAN
1025#define WIN32_LEAN_AND_MEAN
1026#endif
1027#ifndef NOMINMAX
1028#define NOMINMAX
1029#endif
1030#ifndef __MINGW32__
1031#include <Windows.h> // _wfopen, OpenClipboard
1032#else
1033#include <windows.h>
1034#endif
1035#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
1036// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1037#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1038#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1039#endif
1040#endif
1041
1042// [Apple] OS specific includes
1043#if defined(__APPLE__)
1044#include <TargetConditionals.h>
1045#endif
1046
1047// Visual Studio warnings
1048#ifdef _MSC_VER
1049#pragma warning (disable: 4127) // condition expression is constant
1050#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1051#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
1052#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
1053#endif
1054#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).
1055#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1056#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1057#endif
1058
1059// Clang/GCC warnings with -Weverything
1060#if defined(__clang__)
1061#if __has_warning("-Wunknown-warning-option")
1062#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!
1063#endif
1064#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
1065#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
1066#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.
1067#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.
1068#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.
1069#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
1070#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
1071#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.
1072#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
1073#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
1074#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.
1075#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
1076#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
1077#elif defined(__GNUC__)
1078// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1079#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
1080#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
1081#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
1082#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
1083#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
1084#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1085#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
1086#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1087#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
1088#endif
1089
1090// Debug options
1091#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
1092#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
1093
1094// 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.
1095static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
1096static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
1097
1098static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut.
1099
1100// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
1101static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
1102static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
1103static 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.
1104
1105// Tooltip offset
1106static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
1107
1108// Docking
1109static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
1110
1111//-------------------------------------------------------------------------
1112// [SECTION] FORWARD DECLARATIONS
1113//-------------------------------------------------------------------------
1114
1115static void SetCurrentWindow(ImGuiWindow* window);
1116static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1117static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1118
1119static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1120
1121// Settings
1122static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1123static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1124static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1125static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1126static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1127
1128// Platform Dependents default implementation for IO functions
1129static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx);
1130static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text);
1131static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1132
1133namespace ImGui
1134{
1135// Item
1136static void ItemHandleShortcut(ImGuiID id);
1137
1138// Navigation
1139static void NavUpdate();
1140static void NavUpdateWindowing();
1141static void NavUpdateWindowingOverlay();
1142static void NavUpdateCancelRequest();
1143static void NavUpdateCreateMoveRequest();
1144static void NavUpdateCreateTabbingRequest();
1145static float NavUpdatePageUpPageDown();
1146static inline void NavUpdateAnyRequestFlag();
1147static void NavUpdateCreateWrappingRequest();
1148static void NavEndFrame();
1149static bool NavScoreItem(ImGuiNavItemData* result);
1150static void NavApplyItemToResult(ImGuiNavItemData* result);
1151static void NavProcessItem();
1152static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1153static ImVec2 NavCalcPreferredRefPos();
1154static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1155static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1156static void NavRestoreLayer(ImGuiNavLayer layer);
1157static int FindWindowFocusIndex(ImGuiWindow* window);
1158
1159// Error Checking and Debug Tools
1160static void ErrorCheckNewFrameSanityChecks();
1161static void ErrorCheckEndFrameSanityChecks();
1162static void UpdateDebugToolItemPicker();
1163static void UpdateDebugToolStackQueries();
1164static void UpdateDebugToolFlashStyleColor();
1165
1166// Inputs
1167static void UpdateKeyboardInputs();
1168static void UpdateMouseInputs();
1169static void UpdateMouseWheel();
1170static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1171
1172// Misc
1173static void UpdateSettings();
1174static 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);
1175static void RenderWindowOuterBorders(ImGuiWindow* window);
1176static 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);
1177static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1178static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1179static void RenderDimmedBackgrounds();
1180static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1181
1182// Viewports
1183const 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.
1184static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags);
1185static void DestroyViewport(ImGuiViewportP* viewport);
1186static void UpdateViewportsNewFrame();
1187static void UpdateViewportsEndFrame();
1188static void WindowSelectViewport(ImGuiWindow* window);
1189static void WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack);
1190static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport);
1191static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window);
1192static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window);
1193static int FindPlatformMonitorForPos(const ImVec2& pos);
1194static int FindPlatformMonitorForRect(const ImRect& r);
1195static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport);
1196
1197}
1198
1199//-----------------------------------------------------------------------------
1200// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1201//-----------------------------------------------------------------------------
1202
1203// DLL users:
1204// - Heaps and globals are not shared across DLL boundaries!
1205// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1206// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1207// - 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.
1208// - 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).
1209
1210// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1211// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1212// Change to a different context by calling ImGui::SetCurrentContext().
1213// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1214// If you want thread-safety to allow N threads to access N different contexts:
1215// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1216// struct ImGuiContext;
1217// extern thread_local ImGuiContext* MyImGuiTLS;
1218// #define GImGui MyImGuiTLS
1219// 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.
1220// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1221// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1222// - DLL users: read comments above.
1223#ifndef GImGui
1224ImGuiContext* GImGui = NULL;
1225#endif
1226
1227// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1228// - 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.
1229// - DLL users: read comments above.
1230#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1231static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
1232static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
1233#else
1234static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1235static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1236#endif
1237static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
1238static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
1239static void* GImAllocatorUserData = NULL;
1240
1241//-----------------------------------------------------------------------------
1242// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
1243//-----------------------------------------------------------------------------
1244
1245ImGuiStyle::ImGuiStyle()
1246{
1247 Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
1248 DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1249 WindowPadding = ImVec2(8,8); // Padding within a window
1250 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.
1251 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1252 WindowMinSize = ImVec2(32,32); // Minimum window size
1253 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1254 WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1255 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1256 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1257 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1258 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1259 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1260 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1261 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1262 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1263 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1264 CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1265 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!
1266 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1267 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1268 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1269 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1270 GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
1271 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1272 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1273 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1274 TabBorderSize = 0.0f; // Thickness of border around tabs.
1275 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.
1276 TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1277 TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1278 TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1279 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1280 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1281 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.
1282 SeparatorTextBorderSize = 3.0f; // Thickkness of border in SeparatorText()
1283 SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1284 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.
1285 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.
1286 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.
1287 DockingSeparatorSize = 2.0f; // Thickness of resizing border between docked windows
1288 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1289 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1290 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1291 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1292 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.
1293 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.
1294
1295 // Behaviors
1296 HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1297 HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1298 HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1299 HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1300 HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1301
1302 // Default theme
1303 ImGui::StyleColorsDark(this);
1304}
1305
1306// 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.
1307// 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.
1308void ImGuiStyle::ScaleAllSizes(float scale_factor)
1309{
1310 WindowPadding = ImTrunc(WindowPadding * scale_factor);
1311 WindowRounding = ImTrunc(WindowRounding * scale_factor);
1312 WindowMinSize = ImTrunc(WindowMinSize * scale_factor);
1313 ChildRounding = ImTrunc(ChildRounding * scale_factor);
1314 PopupRounding = ImTrunc(PopupRounding * scale_factor);
1315 FramePadding = ImTrunc(FramePadding * scale_factor);
1316 FrameRounding = ImTrunc(FrameRounding * scale_factor);
1317 ItemSpacing = ImTrunc(ItemSpacing * scale_factor);
1318 ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);
1319 CellPadding = ImTrunc(CellPadding * scale_factor);
1320 TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);
1321 IndentSpacing = ImTrunc(IndentSpacing * scale_factor);
1322 ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);
1323 ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);
1324 ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);
1325 GrabMinSize = ImTrunc(GrabMinSize * scale_factor);
1326 GrabRounding = ImTrunc(GrabRounding * scale_factor);
1327 LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);
1328 TabRounding = ImTrunc(TabRounding * scale_factor);
1329 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1330 SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);
1331 DockingSeparatorSize = ImTrunc(DockingSeparatorSize * scale_factor);
1332 DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);
1333 DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);
1334 MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);
1335}
1336
1337ImGuiIO::ImGuiIO()
1338{
1339 // Most fields are initialized with zero
1340 memset(this, 0, sizeof(*this));
1341 IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1342
1343 // Settings
1344 ConfigFlags = ImGuiConfigFlags_None;
1345 BackendFlags = ImGuiBackendFlags_None;
1346 DisplaySize = ImVec2(-1.0f, -1.0f);
1347 DeltaTime = 1.0f / 60.0f;
1348 IniSavingRate = 5.0f;
1349 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).
1350 LogFilename = "imgui_log.txt";
1351#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1352 for (int i = 0; i < ImGuiKey_COUNT; i++)
1353 KeyMap[i] = -1;
1354#endif
1355 UserData = NULL;
1356
1357 Fonts = NULL;
1358 FontGlobalScale = 1.0f;
1359 FontDefault = NULL;
1360 FontAllowUserScaling = false;
1361 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1362
1363 // Docking options (when ImGuiConfigFlags_DockingEnable is set)
1364 ConfigDockingNoSplit = false;
1365 ConfigDockingWithShift = false;
1366 ConfigDockingAlwaysTabBar = false;
1367 ConfigDockingTransparentPayload = false;
1368
1369 // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set)
1370 ConfigViewportsNoAutoMerge = false;
1371 ConfigViewportsNoTaskBarIcon = false;
1372 ConfigViewportsNoDecoration = true;
1373 ConfigViewportsNoDefaultParent = false;
1374
1375 // Miscellaneous options
1376 MouseDrawCursor = false;
1377#ifdef __APPLE__
1378 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1379#else
1380 ConfigMacOSXBehaviors = false;
1381#endif
1382 ConfigInputTrickleEventQueue = true;
1383 ConfigInputTextCursorBlink = true;
1384 ConfigInputTextEnterKeepActive = false;
1385 ConfigDragClickToInputText = false;
1386 ConfigWindowsResizeFromEdges = true;
1387 ConfigWindowsMoveFromTitleBarOnly = false;
1388 ConfigMemoryCompactTimer = 60.0f;
1389 ConfigDebugBeginReturnValueOnce = false;
1390 ConfigDebugBeginReturnValueLoop = false;
1391
1392 // Inputs Behaviors
1393 MouseDoubleClickTime = 0.30f;
1394 MouseDoubleClickMaxDist = 6.0f;
1395 MouseDragThreshold = 6.0f;
1396 KeyRepeatDelay = 0.275f;
1397 KeyRepeatRate = 0.050f;
1398
1399 // Platform Functions
1400 // Note: Initialize() will setup default clipboard/ime handlers.
1401 BackendPlatformName = BackendRendererName = NULL;
1402 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1403 PlatformLocaleDecimalPoint = '.';
1404
1405 // Input (NB: we already have memset zero the entire structure!)
1406 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1407 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1408 MouseSource = ImGuiMouseSource_Mouse;
1409 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1410 for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1411 AppAcceptingEvents = true;
1412 BackendUsingLegacyKeyArrays = (ImS8)-1;
1413 BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong
1414}
1415
1416// Pass in translated ASCII characters for text input.
1417// - with glfw you can get those from the callback set in glfwSetCharCallback()
1418// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1419// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1420void ImGuiIO::AddInputCharacter(unsigned int c)
1421{
1422 IM_ASSERT(Ctx != NULL);
1423 ImGuiContext& g = *Ctx;
1424 if (c == 0 || !AppAcceptingEvents)
1425 return;
1426
1427 ImGuiInputEvent e;
1428 e.Type = ImGuiInputEventType_Text;
1429 e.Source = ImGuiInputSource_Keyboard;
1430 e.EventId = g.InputEventsNextEventId++;
1431 e.Text.Char = c;
1432 g.InputEventsQueue.push_back(e);
1433}
1434
1435// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1436// we should save the high surrogate.
1437void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1438{
1439 if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1440 return;
1441
1442 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1443 {
1444 if (InputQueueSurrogate != 0)
1445 AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1446 InputQueueSurrogate = c;
1447 return;
1448 }
1449
1450 ImWchar cp = c;
1451 if (InputQueueSurrogate != 0)
1452 {
1453 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1454 {
1455 AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1456 }
1457 else
1458 {
1459#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1460 cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1461#else
1462 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1463#endif
1464 }
1465
1466 InputQueueSurrogate = 0;
1467 }
1468 AddInputCharacter((unsigned)cp);
1469}
1470
1471void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1472{
1473 if (!AppAcceptingEvents)
1474 return;
1475 while (*utf8_chars != 0)
1476 {
1477 unsigned int c = 0;
1478 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1479 AddInputCharacter(c);
1480 }
1481}
1482
1483// Clear all incoming events.
1484void ImGuiIO::ClearEventsQueue()
1485{
1486 IM_ASSERT(Ctx != NULL);
1487 ImGuiContext& g = *Ctx;
1488 g.InputEventsQueue.clear();
1489}
1490
1491// Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1492void ImGuiIO::ClearInputKeys()
1493{
1494#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1495 memset(KeysDown, 0, sizeof(KeysDown));
1496#endif
1497 for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
1498 {
1499 KeysData[n].Down = false;
1500 KeysData[n].DownDuration = -1.0f;
1501 KeysData[n].DownDurationPrev = -1.0f;
1502 }
1503 KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1504 KeyMods = ImGuiMod_None;
1505 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1506 for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
1507 {
1508 MouseDown[n] = false;
1509 MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1510 }
1511 MouseWheel = MouseWheelH = 0.0f;
1512 InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
1513}
1514
1515// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
1516// Current frame character buffer is now also cleared by ClearInputKeys().
1517#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1518void ImGuiIO::ClearInputCharacters()
1519{
1520 InputQueueCharacters.resize(0);
1521}
1522#endif
1523
1524static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1525{
1526 ImGuiContext& g = *ctx;
1527 for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1528 {
1529 ImGuiInputEvent* e = &g.InputEventsQueue[n];
1530 if (e->Type != type)
1531 continue;
1532 if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1533 continue;
1534 if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1535 continue;
1536 return e;
1537 }
1538 return NULL;
1539}
1540
1541// Queue a new key down/up event.
1542// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1543// - bool down: Is the key down? use false to signify a key release.
1544// - float analog_value: 0.0f..1.0f
1545// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1546// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1547void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1548{
1549 //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); }
1550 IM_ASSERT(Ctx != NULL);
1551 if (key == ImGuiKey_None || !AppAcceptingEvents)
1552 return;
1553 ImGuiContext& g = *Ctx;
1554 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.
1555 IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1556
1557 // MacOS: swap Cmd(Super) and Ctrl
1558 if (g.IO.ConfigMacOSXBehaviors)
1559 {
1560 if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; }
1561 else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; }
1562 else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1563 else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1564 else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
1565 else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1566 }
1567
1568 // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
1569#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1570 IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1571 if (BackendUsingLegacyKeyArrays == -1)
1572 for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
1573 IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
1574 BackendUsingLegacyKeyArrays = 0;
1575#endif
1576 if (ImGui::IsGamepadKey(key))
1577 BackendUsingLegacyNavInputArray = false;
1578
1579 // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1580 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
1581 const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
1582 const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
1583 const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
1584 if (latest_key_down == down && latest_key_analog == analog_value)
1585 return;
1586
1587 // Add event
1588 ImGuiInputEvent e;
1589 e.Type = ImGuiInputEventType_Key;
1590 e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
1591 e.EventId = g.InputEventsNextEventId++;
1592 e.Key.Key = key;
1593 e.Key.Down = down;
1594 e.Key.AnalogValue = analog_value;
1595 g.InputEventsQueue.push_back(e);
1596}
1597
1598void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1599{
1600 if (!AppAcceptingEvents)
1601 return;
1602 AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
1603}
1604
1605// [Optional] Call after AddKeyEvent().
1606// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
1607// 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.
1608void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
1609{
1610 if (key == ImGuiKey_None)
1611 return;
1612 IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
1613 IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
1614 IM_UNUSED(native_keycode); // Yet unused
1615 IM_UNUSED(native_scancode); // Yet unused
1616
1617 // Build native->imgui map so old user code can still call key functions with native 0..511 values.
1618#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
1619 const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;
1620 if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key))
1621 return;
1622 KeyMap[legacy_key] = key;
1623 KeyMap[key] = legacy_key;
1624#else
1625 IM_UNUSED(key);
1626 IM_UNUSED(native_legacy_index);
1627#endif
1628}
1629
1630// 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.
1631void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1632{
1633 AppAcceptingEvents = accepting_events;
1634}
1635
1636// Queue a mouse move event
1637void ImGuiIO::AddMousePosEvent(float x, float y)
1638{
1639 IM_ASSERT(Ctx != NULL);
1640 ImGuiContext& g = *Ctx;
1641 if (!AppAcceptingEvents)
1642 return;
1643
1644 // Apply same flooring as UpdateMouseInputs()
1645 ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
1646
1647 // Filter duplicate
1648 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
1649 const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1650 if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1651 return;
1652
1653 ImGuiInputEvent e;
1654 e.Type = ImGuiInputEventType_MousePos;
1655 e.Source = ImGuiInputSource_Mouse;
1656 e.EventId = g.InputEventsNextEventId++;
1657 e.MousePos.PosX = pos.x;
1658 e.MousePos.PosY = pos.y;
1659 e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1660 g.InputEventsQueue.push_back(e);
1661}
1662
1663void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1664{
1665 IM_ASSERT(Ctx != NULL);
1666 ImGuiContext& g = *Ctx;
1667 IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1668 if (!AppAcceptingEvents)
1669 return;
1670
1671 // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1672 if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1673 {
1674 // Order of both statements matterns: this event will still release mouse button 1
1675 mouse_button = 1;
1676 if (!down)
1677 MouseCtrlLeftAsRightClick = false;
1678 }
1679
1680 // Filter duplicate
1681 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
1682 const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1683 if (latest_button_down == down)
1684 return;
1685
1686 // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1687 // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1688 // - At this point we want from !down to down, so this is handling the initial press.
1689 if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1690 {
1691 const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
1692 if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1693 {
1694 IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1695 MouseCtrlLeftAsRightClick = true;
1696 AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1697 return;
1698 }
1699 }
1700
1701 ImGuiInputEvent e;
1702 e.Type = ImGuiInputEventType_MouseButton;
1703 e.Source = ImGuiInputSource_Mouse;
1704 e.EventId = g.InputEventsNextEventId++;
1705 e.MouseButton.Button = mouse_button;
1706 e.MouseButton.Down = down;
1707 e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1708 g.InputEventsQueue.push_back(e);
1709}
1710
1711// Queue a mouse wheel event (some mouse/API may only have a Y component)
1712void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1713{
1714 IM_ASSERT(Ctx != NULL);
1715 ImGuiContext& g = *Ctx;
1716
1717 // Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1718 if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1719 return;
1720
1721 ImGuiInputEvent e;
1722 e.Type = ImGuiInputEventType_MouseWheel;
1723 e.Source = ImGuiInputSource_Mouse;
1724 e.EventId = g.InputEventsNextEventId++;
1725 e.MouseWheel.WheelX = wheel_x;
1726 e.MouseWheel.WheelY = wheel_y;
1727 e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1728 g.InputEventsQueue.push_back(e);
1729}
1730
1731// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1732// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1733void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1734{
1735 IM_ASSERT(Ctx != NULL);
1736 ImGuiContext& g = *Ctx;
1737 g.InputEventsNextMouseSource = source;
1738}
1739
1740void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id)
1741{
1742 IM_ASSERT(Ctx != NULL);
1743 ImGuiContext& g = *Ctx;
1744 //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport);
1745 if (!AppAcceptingEvents)
1746 return;
1747
1748 // Filter duplicate
1749 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseViewport);
1750 const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport;
1751 if (latest_viewport_id == viewport_id)
1752 return;
1753
1754 ImGuiInputEvent e;
1755 e.Type = ImGuiInputEventType_MouseViewport;
1756 e.Source = ImGuiInputSource_Mouse;
1757 e.MouseViewport.HoveredViewportID = viewport_id;
1758 g.InputEventsQueue.push_back(e);
1759}
1760
1761void ImGuiIO::AddFocusEvent(bool focused)
1762{
1763 IM_ASSERT(Ctx != NULL);
1764 ImGuiContext& g = *Ctx;
1765
1766 // Filter duplicate
1767 const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
1768 const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1769 if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1770 return;
1771
1772 ImGuiInputEvent e;
1773 e.Type = ImGuiInputEventType_Focus;
1774 e.EventId = g.InputEventsNextEventId++;
1775 e.AppFocused.Focused = focused;
1776 g.InputEventsQueue.push_back(e);
1777}
1778
1779//-----------------------------------------------------------------------------
1780// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1781//-----------------------------------------------------------------------------
1782
1783ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1784{
1785 IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1786 ImVec2 p_last = p1;
1787 ImVec2 p_closest;
1788 float p_closest_dist2 = FLT_MAX;
1789 float t_step = 1.0f / (float)num_segments;
1790 for (int i_step = 1; i_step <= num_segments; i_step++)
1791 {
1792 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1793 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1794 float dist2 = ImLengthSqr(p - p_line);
1795 if (dist2 < p_closest_dist2)
1796 {
1797 p_closest = p_line;
1798 p_closest_dist2 = dist2;
1799 }
1800 p_last = p_current;
1801 }
1802 return p_closest;
1803}
1804
1805// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1806static 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)
1807{
1808 float dx = x4 - x1;
1809 float dy = y4 - y1;
1810 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1811 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1812 d2 = (d2 >= 0) ? d2 : -d2;
1813 d3 = (d3 >= 0) ? d3 : -d3;
1814 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1815 {
1816 ImVec2 p_current(x4, y4);
1817 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1818 float dist2 = ImLengthSqr(p - p_line);
1819 if (dist2 < p_closest_dist2)
1820 {
1821 p_closest = p_line;
1822 p_closest_dist2 = dist2;
1823 }
1824 p_last = p_current;
1825 }
1826 else if (level < 10)
1827 {
1828 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1829 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1830 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1831 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1832 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1833 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1834 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1835 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1836 }
1837}
1838
1839// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1840// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
1841ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1842{
1843 IM_ASSERT(tess_tol > 0.0f);
1844 ImVec2 p_last = p1;
1845 ImVec2 p_closest;
1846 float p_closest_dist2 = FLT_MAX;
1847 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);
1848 return p_closest;
1849}
1850
1851ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1852{
1853 ImVec2 ap = p - a;
1854 ImVec2 ab_dir = b - a;
1855 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1856 if (dot < 0.0f)
1857 return a;
1858 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1859 if (dot > ab_len_sqr)
1860 return b;
1861 return a + ab_dir * dot / ab_len_sqr;
1862}
1863
1864bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1865{
1866 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1867 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1868 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1869 return ((b1 == b2) && (b2 == b3));
1870}
1871
1872void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1873{
1874 ImVec2 v0 = b - a;
1875 ImVec2 v1 = c - a;
1876 ImVec2 v2 = p - a;
1877 const float denom = v0.x * v1.y - v1.x * v0.y;
1878 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1879 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1880 out_u = 1.0f - out_v - out_w;
1881}
1882
1883ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1884{
1885 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1886 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1887 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1888 float dist2_ab = ImLengthSqr(p - proj_ab);
1889 float dist2_bc = ImLengthSqr(p - proj_bc);
1890 float dist2_ca = ImLengthSqr(p - proj_ca);
1891 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1892 if (m == dist2_ab)
1893 return proj_ab;
1894 if (m == dist2_bc)
1895 return proj_bc;
1896 return proj_ca;
1897}
1898
1899//-----------------------------------------------------------------------------
1900// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1901//-----------------------------------------------------------------------------
1902
1903// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
1904int ImStricmp(const char* str1, const char* str2)
1905{
1906 int d;
1907 while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
1908 return d;
1909}
1910
1911int ImStrnicmp(const char* str1, const char* str2, size_t count)
1912{
1913 int d = 0;
1914 while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1915 return d;
1916}
1917
1918void ImStrncpy(char* dst, const char* src, size_t count)
1919{
1920 if (count < 1)
1921 return;
1922 if (count > 1)
1923 strncpy(dst, src, count - 1);
1924 dst[count - 1] = 0;
1925}
1926
1927char* ImStrdup(const char* str)
1928{
1929 size_t len = strlen(str);
1930 void* buf = IM_ALLOC(len + 1);
1931 return (char*)memcpy(buf, (const void*)str, len + 1);
1932}
1933
1934char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1935{
1936 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1937 size_t src_size = strlen(src) + 1;
1938 if (dst_buf_size < src_size)
1939 {
1940 IM_FREE(dst);
1941 dst = (char*)IM_ALLOC(src_size);
1942 if (p_dst_size)
1943 *p_dst_size = src_size;
1944 }
1945 return (char*)memcpy(dst, (const void*)src, src_size);
1946}
1947
1948const char* ImStrchrRange(const char* str, const char* str_end, char c)
1949{
1950 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1951 return p;
1952}
1953
1954int ImStrlenW(const ImWchar* str)
1955{
1956 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1957 int n = 0;
1958 while (*str++) n++;
1959 return n;
1960}
1961
1962// Find end-of-line. Return pointer will point to either first \n, either str_end.
1963const char* ImStreolRange(const char* str, const char* str_end)
1964{
1965 const char* p = (const char*)memchr(str, '\n', str_end - str);
1966 return p ? p : str_end;
1967}
1968
1969const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1970{
1971 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1972 buf_mid_line--;
1973 return buf_mid_line;
1974}
1975
1976const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1977{
1978 if (!needle_end)
1979 needle_end = needle + strlen(needle);
1980
1981 const char un0 = (char)ImToUpper(*needle);
1982 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1983 {
1984 if (ImToUpper(*haystack) == un0)
1985 {
1986 const char* b = needle + 1;
1987 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1988 if (ImToUpper(*a) != ImToUpper(*b))
1989 break;
1990 if (b == needle_end)
1991 return haystack;
1992 }
1993 haystack++;
1994 }
1995 return NULL;
1996}
1997
1998// 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.
1999void ImStrTrimBlanks(char* buf)
2000{
2001 char* p = buf;
2002 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
2003 p++;
2004 char* p_start = p;
2005 while (*p != 0) // Find end of string
2006 p++;
2007 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
2008 p--;
2009 if (p_start != buf) // Copy memory if we had leading blanks
2010 memmove(buf, p_start, p - p_start);
2011 buf[p - p_start] = 0; // Zero terminate
2012}
2013
2014const char* ImStrSkipBlank(const char* str)
2015{
2016 while (str[0] == ' ' || str[0] == '\t')
2017 str++;
2018 return str;
2019}
2020
2021// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
2022// 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.
2023// B) When buf==NULL vsnprintf() will return the output size.
2024#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2025
2026// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
2027// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2028// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
2029// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
2030#ifdef IMGUI_USE_STB_SPRINTF
2031#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
2032#define STB_SPRINTF_IMPLEMENTATION
2033#endif
2034#ifdef IMGUI_STB_SPRINTF_FILENAME
2035#include IMGUI_STB_SPRINTF_FILENAME
2036#else
2037#include "stb_sprintf.h"
2038#endif
2039#endif // #ifdef IMGUI_USE_STB_SPRINTF
2040
2041#if defined(_MSC_VER) && !defined(vsnprintf)
2042#define vsnprintf _vsnprintf
2043#endif
2044
2045int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
2046{
2047 va_list args;
2048 va_start(args, fmt);
2049#ifdef IMGUI_USE_STB_SPRINTF
2050 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2051#else
2052 int w = vsnprintf(buf, buf_size, fmt, args);
2053#endif
2054 va_end(args);
2055 if (buf == NULL)
2056 return w;
2057 if (w == -1 || w >= (int)buf_size)
2058 w = (int)buf_size - 1;
2059 buf[w] = 0;
2060 return w;
2061}
2062
2063int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
2064{
2065#ifdef IMGUI_USE_STB_SPRINTF
2066 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2067#else
2068 int w = vsnprintf(buf, buf_size, fmt, args);
2069#endif
2070 if (buf == NULL)
2071 return w;
2072 if (w == -1 || w >= (int)buf_size)
2073 w = (int)buf_size - 1;
2074 buf[w] = 0;
2075 return w;
2076}
2077#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2078
2079void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
2080{
2081 va_list args;
2082 va_start(args, fmt);
2083 ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
2084 va_end(args);
2085}
2086
2087void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
2088{
2089 ImGuiContext& g = *GImGui;
2090 if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
2091 {
2092 const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
2093 if (buf == NULL)
2094 buf = "(null)";
2095 *out_buf = buf;
2096 if (out_buf_end) { *out_buf_end = buf + strlen(buf); }
2097 }
2098 else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
2099 {
2100 int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
2101 const char* buf = va_arg(args, const char*);
2102 if (buf == NULL)
2103 {
2104 buf = "(null)";
2105 buf_len = ImMin(buf_len, 6);
2106 }
2107 *out_buf = buf;
2108 *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
2109 }
2110 else
2111 {
2112 int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
2113 *out_buf = g.TempBuffer.Data;
2114 if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
2115 }
2116}
2117
2118// CRC32 needs a 1KB lookup table (not cache friendly)
2119// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
2120// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
2121static const ImU32 GCrc32LookupTable[256] =
2122{
2123 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
2124 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
2125 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
2126 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
2127 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
2128 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
2129 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
2130 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
2131 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
2132 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
2133 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
2134 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
2135 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
2136 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
2137 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
2138 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
2139};
2140
2141// Known size hash
2142// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
2143// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2144ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
2145{
2146 ImU32 crc = ~seed;
2147 const unsigned char* data = (const unsigned char*)data_p;
2148 const ImU32* crc32_lut = GCrc32LookupTable;
2149 while (data_size-- != 0)
2150 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
2151 return ~crc;
2152}
2153
2154// Zero-terminated string hash, with support for ### to reset back to seed value
2155// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
2156// Because this syntax is rarely used we are optimizing for the common case.
2157// - If we reach ### in the string we discard the hash so far and reset to the seed.
2158// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
2159// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2160ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
2161{
2162 seed = ~seed;
2163 ImU32 crc = seed;
2164 const unsigned char* data = (const unsigned char*)data_p;
2165 const ImU32* crc32_lut = GCrc32LookupTable;
2166 if (data_size != 0)
2167 {
2168 while (data_size-- != 0)
2169 {
2170 unsigned char c = *data++;
2171 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
2172 crc = seed;
2173 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2174 }
2175 }
2176 else
2177 {
2178 while (unsigned char c = *data++)
2179 {
2180 if (c == '#' && data[0] == '#' && data[1] == '#')
2181 crc = seed;
2182 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2183 }
2184 }
2185 return ~crc;
2186}
2187
2188//-----------------------------------------------------------------------------
2189// [SECTION] MISC HELPERS/UTILITIES (File functions)
2190//-----------------------------------------------------------------------------
2191
2192// Default file functions
2193#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2194
2195ImFileHandle ImFileOpen(const char* filename, const char* mode)
2196{
2197#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
2198 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
2199 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
2200 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
2201 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
2202
2203 // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
2204 // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
2205 wchar_t local_temp_stack[FILENAME_MAX];
2206 ImVector<wchar_t> local_temp_heap;
2207 if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
2208 local_temp_heap.resize(filename_wsize + mode_wsize);
2209 wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
2210 wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
2211 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
2212 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
2213 return ::_wfopen(filename_wbuf, mode_wbuf);
2214#else
2215 return fopen(filename, mode);
2216#endif
2217}
2218
2219// 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.
2220bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
2221ImU64 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; }
2222ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
2223ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
2224#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2225
2226// Helper: Load file content into memory
2227// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
2228// This can't really be used with "rt" because fseek size won't match read size.
2229void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
2230{
2231 IM_ASSERT(filename && mode);
2232 if (out_file_size)
2233 *out_file_size = 0;
2234
2235 ImFileHandle f;
2236 if ((f = ImFileOpen(filename, mode)) == NULL)
2237 return NULL;
2238
2239 size_t file_size = (size_t)ImFileGetSize(f);
2240 if (file_size == (size_t)-1)
2241 {
2242 ImFileClose(f);
2243 return NULL;
2244 }
2245
2246 void* file_data = IM_ALLOC(file_size + padding_bytes);
2247 if (file_data == NULL)
2248 {
2249 ImFileClose(f);
2250 return NULL;
2251 }
2252 if (ImFileRead(file_data, 1, file_size, f) != file_size)
2253 {
2254 ImFileClose(f);
2255 IM_FREE(file_data);
2256 return NULL;
2257 }
2258 if (padding_bytes > 0)
2259 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
2260
2261 ImFileClose(f);
2262 if (out_file_size)
2263 *out_file_size = file_size;
2264
2265 return file_data;
2266}
2267
2268//-----------------------------------------------------------------------------
2269// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
2270//-----------------------------------------------------------------------------
2271
2272IM_MSVC_RUNTIME_CHECKS_OFF
2273
2274// Convert UTF-8 to 32-bit character, process single character input.
2275// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
2276// We handle UTF-8 decoding error by skipping forward.
2277int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
2278{
2279 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 };
2280 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
2281 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
2282 static const int shiftc[] = { 0, 18, 12, 6, 0 };
2283 static const int shifte[] = { 0, 6, 4, 2, 0 };
2284 int len = lengths[*(const unsigned char*)in_text >> 3];
2285 int wanted = len + (len ? 0 : 1);
2286
2287 if (in_text_end == NULL)
2288 in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
2289
2290 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
2291 // so it is fast even with excessive branching.
2292 unsigned char s[4];
2293 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
2294 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
2295 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
2296 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
2297
2298 // Assume a four-byte character and load four bytes. Unused bits are shifted out.
2299 *out_char = (uint32_t)(s[0] & masks[len]) << 18;
2300 *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
2301 *out_char |= (uint32_t)(s[2] & 0x3f) << 6;
2302 *out_char |= (uint32_t)(s[3] & 0x3f) << 0;
2303 *out_char >>= shiftc[len];
2304
2305 // Accumulate the various error conditions.
2306 int e = 0;
2307 e = (*out_char < mins[len]) << 6; // non-canonical encoding
2308 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
2309 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range?
2310 e |= (s[1] & 0xc0) >> 2;
2311 e |= (s[2] & 0xc0) >> 4;
2312 e |= (s[3] ) >> 6;
2313 e ^= 0x2a; // top two bits of each tail byte correct?
2314 e >>= shifte[len];
2315
2316 if (e)
2317 {
2318 // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
2319 // One byte is consumed in case of invalid first byte of in_text.
2320 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
2321 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
2322 wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
2323 *out_char = IM_UNICODE_CODEPOINT_INVALID;
2324 }
2325
2326 return wanted;
2327}
2328
2329int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
2330{
2331 ImWchar* buf_out = buf;
2332 ImWchar* buf_end = buf + buf_size;
2333 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2334 {
2335 unsigned int c;
2336 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2337 *buf_out++ = (ImWchar)c;
2338 }
2339 *buf_out = 0;
2340 if (in_text_remaining)
2341 *in_text_remaining = in_text;
2342 return (int)(buf_out - buf);
2343}
2344
2345int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
2346{
2347 int char_count = 0;
2348 while ((!in_text_end || in_text < in_text_end) && *in_text)
2349 {
2350 unsigned int c;
2351 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2352 char_count++;
2353 }
2354 return char_count;
2355}
2356
2357// Based on stb_to_utf8() from github.com/nothings/stb/
2358static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
2359{
2360 if (c < 0x80)
2361 {
2362 buf[0] = (char)c;
2363 return 1;
2364 }
2365 if (c < 0x800)
2366 {
2367 if (buf_size < 2) return 0;
2368 buf[0] = (char)(0xc0 + (c >> 6));
2369 buf[1] = (char)(0x80 + (c & 0x3f));
2370 return 2;
2371 }
2372 if (c < 0x10000)
2373 {
2374 if (buf_size < 3) return 0;
2375 buf[0] = (char)(0xe0 + (c >> 12));
2376 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
2377 buf[2] = (char)(0x80 + ((c ) & 0x3f));
2378 return 3;
2379 }
2380 if (c <= 0x10FFFF)
2381 {
2382 if (buf_size < 4) return 0;
2383 buf[0] = (char)(0xf0 + (c >> 18));
2384 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
2385 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
2386 buf[3] = (char)(0x80 + ((c ) & 0x3f));
2387 return 4;
2388 }
2389 // Invalid code point, the max unicode is 0x10FFFF
2390 return 0;
2391}
2392
2393const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
2394{
2395 int count = ImTextCharToUtf8_inline(out_buf, 5, c);
2396 out_buf[count] = 0;
2397 return out_buf;
2398}
2399
2400// Not optimal but we very rarely use this function.
2401int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
2402{
2403 unsigned int unused = 0;
2404 return ImTextCharFromUtf8(&unused, in_text, in_text_end);
2405}
2406
2407static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
2408{
2409 if (c < 0x80) return 1;
2410 if (c < 0x800) return 2;
2411 if (c < 0x10000) return 3;
2412 if (c <= 0x10FFFF) return 4;
2413 return 3;
2414}
2415
2416int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
2417{
2418 char* buf_p = out_buf;
2419 const char* buf_end = out_buf + out_buf_size;
2420 while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2421 {
2422 unsigned int c = (unsigned int)(*in_text++);
2423 if (c < 0x80)
2424 *buf_p++ = (char)c;
2425 else
2426 buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
2427 }
2428 *buf_p = 0;
2429 return (int)(buf_p - out_buf);
2430}
2431
2432int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
2433{
2434 int bytes_count = 0;
2435 while ((!in_text_end || in_text < in_text_end) && *in_text)
2436 {
2437 unsigned int c = (unsigned int)(*in_text++);
2438 if (c < 0x80)
2439 bytes_count++;
2440 else
2441 bytes_count += ImTextCountUtf8BytesFromChar(c);
2442 }
2443 return bytes_count;
2444}
2445
2446const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
2447{
2448 while (in_text_curr > in_text_start)
2449 {
2450 in_text_curr--;
2451 if ((*in_text_curr & 0xC0) != 0x80)
2452 return in_text_curr;
2453 }
2454 return in_text_start;
2455}
2456
2457int ImTextCountLines(const char* in_text, const char* in_text_end)
2458{
2459 if (in_text_end == NULL)
2460 in_text_end = in_text + strlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
2461 int count = 0;
2462 while (in_text < in_text_end)
2463 {
2464 const char* line_end = (const char*)memchr(in_text, '\n', in_text_end - in_text);
2465 in_text = line_end ? line_end + 1 : in_text_end;
2466 count++;
2467 }
2468 return count;
2469}
2470
2471IM_MSVC_RUNTIME_CHECKS_RESTORE
2472
2473//-----------------------------------------------------------------------------
2474// [SECTION] MISC HELPERS/UTILITIES (Color functions)
2475// Note: The Convert functions are early design which are not consistent with other API.
2476//-----------------------------------------------------------------------------
2477
2478IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
2479{
2480 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
2481 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
2482 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
2483 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
2484 return IM_COL32(r, g, b, 0xFF);
2485}
2486
2487ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
2488{
2489 float s = 1.0f / 255.0f;
2490 return ImVec4(
2491 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
2492 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
2493 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
2494 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
2495}
2496
2497ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
2498{
2499 ImU32 out;
2500 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
2501 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
2502 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
2503 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
2504 return out;
2505}
2506
2507// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
2508// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
2509void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
2510{
2511 float K = 0.f;
2512 if (g < b)
2513 {
2514 ImSwap(g, b);
2515 K = -1.f;
2516 }
2517 if (r < g)
2518 {
2519 ImSwap(r, g);
2520 K = -2.f / 6.f - K;
2521 }
2522
2523 const float chroma = r - (g < b ? g : b);
2524 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
2525 out_s = chroma / (r + 1e-20f);
2526 out_v = r;
2527}
2528
2529// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
2530// also http://en.wikipedia.org/wiki/HSL_and_HSV
2531void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
2532{
2533 if (s == 0.0f)
2534 {
2535 // gray
2536 out_r = out_g = out_b = v;
2537 return;
2538 }
2539
2540 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
2541 int i = (int)h;
2542 float f = h - (float)i;
2543 float p = v * (1.0f - s);
2544 float q = v * (1.0f - s * f);
2545 float t = v * (1.0f - s * (1.0f - f));
2546
2547 switch (i)
2548 {
2549 case 0: out_r = v; out_g = t; out_b = p; break;
2550 case 1: out_r = q; out_g = v; out_b = p; break;
2551 case 2: out_r = p; out_g = v; out_b = t; break;
2552 case 3: out_r = p; out_g = q; out_b = v; break;
2553 case 4: out_r = t; out_g = p; out_b = v; break;
2554 case 5: default: out_r = v; out_g = p; out_b = q; break;
2555 }
2556}
2557
2558//-----------------------------------------------------------------------------
2559// [SECTION] ImGuiStorage
2560// Helper: Key->value storage
2561//-----------------------------------------------------------------------------
2562
2563// std::lower_bound but without the bullshit
2564static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
2565{
2566 ImGuiStorage::ImGuiStoragePair* first = data.Data;
2567 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
2568 size_t count = (size_t)(last - first);
2569 while (count > 0)
2570 {
2571 size_t count2 = count >> 1;
2572 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
2573 if (mid->key < key)
2574 {
2575 first = ++mid;
2576 count -= count2 + 1;
2577 }
2578 else
2579 {
2580 count = count2;
2581 }
2582 }
2583 return first;
2584}
2585
2586// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2587void ImGuiStorage::BuildSortByKey()
2588{
2589 struct StaticFunc
2590 {
2591 static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
2592 {
2593 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
2594 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
2595 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
2596 return 0;
2597 }
2598 };
2599 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID);
2600}
2601
2602int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
2603{
2604 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
2605 if (it == Data.end() || it->key != key)
2606 return default_val;
2607 return it->val_i;
2608}
2609
2610bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
2611{
2612 return GetInt(key, default_val ? 1 : 0) != 0;
2613}
2614
2615float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
2616{
2617 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
2618 if (it == Data.end() || it->key != key)
2619 return default_val;
2620 return it->val_f;
2621}
2622
2623void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
2624{
2625 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
2626 if (it == Data.end() || it->key != key)
2627 return NULL;
2628 return it->val_p;
2629}
2630
2631// 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.
2632int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
2633{
2634 ImGuiStoragePair* it = LowerBound(Data, key);
2635 if (it == Data.end() || it->key != key)
2636 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2637 return &it->val_i;
2638}
2639
2640bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2641{
2642 return (bool*)GetIntRef(key, default_val ? 1 : 0);
2643}
2644
2645float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2646{
2647 ImGuiStoragePair* it = LowerBound(Data, key);
2648 if (it == Data.end() || it->key != key)
2649 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2650 return &it->val_f;
2651}
2652
2653void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2654{
2655 ImGuiStoragePair* it = LowerBound(Data, key);
2656 if (it == Data.end() || it->key != key)
2657 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2658 return &it->val_p;
2659}
2660
2661// 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)
2662void ImGuiStorage::SetInt(ImGuiID key, int val)
2663{
2664 ImGuiStoragePair* it = LowerBound(Data, key);
2665 if (it == Data.end() || it->key != key)
2666 Data.insert(it, ImGuiStoragePair(key, val));
2667 else
2668 it->val_i = val;
2669}
2670
2671void ImGuiStorage::SetBool(ImGuiID key, bool val)
2672{
2673 SetInt(key, val ? 1 : 0);
2674}
2675
2676void ImGuiStorage::SetFloat(ImGuiID key, float val)
2677{
2678 ImGuiStoragePair* it = LowerBound(Data, key);
2679 if (it == Data.end() || it->key != key)
2680 Data.insert(it, ImGuiStoragePair(key, val));
2681 else
2682 it->val_f = val;
2683}
2684
2685void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2686{
2687 ImGuiStoragePair* it = LowerBound(Data, key);
2688 if (it == Data.end() || it->key != key)
2689 Data.insert(it, ImGuiStoragePair(key, val));
2690 else
2691 it->val_p = val;
2692}
2693
2694void ImGuiStorage::SetAllInt(int v)
2695{
2696 for (int i = 0; i < Data.Size; i++)
2697 Data[i].val_i = v;
2698}
2699
2700//-----------------------------------------------------------------------------
2701// [SECTION] ImGuiTextFilter
2702//-----------------------------------------------------------------------------
2703
2704// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2705ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
2706{
2707 InputBuf[0] = 0;
2708 CountGrep = 0;
2709 if (default_filter)
2710 {
2711 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2712 Build();
2713 }
2714}
2715
2716bool ImGuiTextFilter::Draw(const char* label, float width)
2717{
2718 if (width != 0.0f)
2719 ImGui::SetNextItemWidth(width);
2720 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2721 if (value_changed)
2722 Build();
2723 return value_changed;
2724}
2725
2726void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2727{
2728 out->resize(0);
2729 const char* wb = b;
2730 const char* we = wb;
2731 while (we < e)
2732 {
2733 if (*we == separator)
2734 {
2735 out->push_back(ImGuiTextRange(wb, we));
2736 wb = we + 1;
2737 }
2738 we++;
2739 }
2740 if (wb != we)
2741 out->push_back(ImGuiTextRange(wb, we));
2742}
2743
2744void ImGuiTextFilter::Build()
2745{
2746 Filters.resize(0);
2747 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2748 input_range.split(',', &Filters);
2749
2750 CountGrep = 0;
2751 for (ImGuiTextRange& f : Filters)
2752 {
2753 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2754 f.b++;
2755 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2756 f.e--;
2757 if (f.empty())
2758 continue;
2759 if (f.b[0] != '-')
2760 CountGrep += 1;
2761 }
2762}
2763
2764bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2765{
2766 if (Filters.empty())
2767 return true;
2768
2769 if (text == NULL)
2770 text = "";
2771
2772 for (const ImGuiTextRange& f : Filters)
2773 {
2774 if (f.empty())
2775 continue;
2776 if (f.b[0] == '-')
2777 {
2778 // Subtract
2779 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2780 return false;
2781 }
2782 else
2783 {
2784 // Grep
2785 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2786 return true;
2787 }
2788 }
2789
2790 // Implicit * grep
2791 if (CountGrep == 0)
2792 return true;
2793
2794 return false;
2795}
2796
2797//-----------------------------------------------------------------------------
2798// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
2799//-----------------------------------------------------------------------------
2800
2801// On some platform vsnprintf() takes va_list by reference and modifies it.
2802// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2803#ifndef va_copy
2804#if defined(__GNUC__) || defined(__clang__)
2805#define va_copy(dest, src) __builtin_va_copy(dest, src)
2806#else
2807#define va_copy(dest, src) (dest = src)
2808#endif
2809#endif
2810
2811char ImGuiTextBuffer::EmptyString[1] = { 0 };
2812
2813void ImGuiTextBuffer::append(const char* str, const char* str_end)
2814{
2815 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2816
2817 // Add zero-terminator the first time
2818 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2819 const int needed_sz = write_off + len;
2820 if (write_off + len >= Buf.Capacity)
2821 {
2822 int new_capacity = Buf.Capacity * 2;
2823 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2824 }
2825
2826 Buf.resize(needed_sz);
2827 memcpy(&Buf[write_off - 1], str, (size_t)len);
2828 Buf[write_off - 1 + len] = 0;
2829}
2830
2831void ImGuiTextBuffer::appendf(const char* fmt, ...)
2832{
2833 va_list args;
2834 va_start(args, fmt);
2835 appendfv(fmt, args);
2836 va_end(args);
2837}
2838
2839// Helper: Text buffer for logging/accumulating text
2840void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2841{
2842 va_list args_copy;
2843 va_copy(args_copy, args);
2844
2845 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2846 if (len <= 0)
2847 {
2848 va_end(args_copy);
2849 return;
2850 }
2851
2852 // Add zero-terminator the first time
2853 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2854 const int needed_sz = write_off + len;
2855 if (write_off + len >= Buf.Capacity)
2856 {
2857 int new_capacity = Buf.Capacity * 2;
2858 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2859 }
2860
2861 Buf.resize(needed_sz);
2862 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2863 va_end(args_copy);
2864}
2865
2866void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
2867{
2868 IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
2869 if (old_size == new_size)
2870 return;
2871 if (EndOffset == 0 || base[EndOffset - 1] == '\n')
2872 LineOffsets.push_back(EndOffset);
2873 const char* base_end = base + new_size;
2874 for (const char* p = base + old_size; (p = (const char*)memchr(p, '\n', base_end - p)) != 0; )
2875 if (++p < base_end) // Don't push a trailing offset on last \n
2876 LineOffsets.push_back((int)(intptr_t)(p - base));
2877 EndOffset = ImMax(EndOffset, new_size);
2878}
2879
2880//-----------------------------------------------------------------------------
2881// [SECTION] ImGuiListClipper
2882//-----------------------------------------------------------------------------
2883
2884// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2885// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
2886static bool GetSkipItemForListClipping()
2887{
2888 ImGuiContext& g = *GImGui;
2889 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2890}
2891
2892static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
2893{
2894 if (ranges.Size - offset <= 1)
2895 return;
2896
2897 // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
2898 for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
2899 for (int i = offset; i < sort_end + offset; ++i)
2900 if (ranges[i].Min > ranges[i + 1].Min)
2901 ImSwap(ranges[i], ranges[i + 1]);
2902
2903 // Now fuse ranges together as much as possible.
2904 for (int i = 1 + offset; i < ranges.Size; i++)
2905 {
2906 IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
2907 if (ranges[i - 1].Max < ranges[i].Min)
2908 continue;
2909 ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
2910 ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
2911 ranges.erase(ranges.Data + i);
2912 i--;
2913 }
2914}
2915
2916static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
2917{
2918 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2919 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2920 // 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?
2921 ImGuiContext& g = *GImGui;
2922 ImGuiWindow* window = g.CurrentWindow;
2923 float off_y = pos_y - window->DC.CursorPos.y;
2924 window->DC.CursorPos.y = pos_y;
2925 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
2926 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.
2927 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.
2928 if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2929 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2930 if (ImGuiTable* table = g.CurrentTable)
2931 {
2932 if (table->IsInsideRow)
2933 ImGui::TableEndRow(table);
2934 table->RowPosY2 = window->DC.CursorPos.y;
2935 const int row_increase = (int)((off_y / line_height) + 0.5f);
2936 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2937 table->RowBgColorCounter += row_increase;
2938 }
2939}
2940
2941static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n)
2942{
2943 // StartPosY starts from ItemsFrozen hence the subtraction
2944 // Perform the add and multiply with double to allow seeking through larger ranges
2945 ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
2946 float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight);
2947 ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight);
2948}
2949
2950ImGuiListClipper::ImGuiListClipper()
2951{
2952 memset(this, 0, sizeof(*this));
2953}
2954
2955ImGuiListClipper::~ImGuiListClipper()
2956{
2957 End();
2958}
2959
2960void ImGuiListClipper::Begin(int items_count, float items_height)
2961{
2962 if (Ctx == NULL)
2963 Ctx = ImGui::GetCurrentContext();
2964
2965 ImGuiContext& g = *Ctx;
2966 ImGuiWindow* window = g.CurrentWindow;
2967 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
2968
2969 if (ImGuiTable* table = g.CurrentTable)
2970 if (table->IsInsideRow)
2971 ImGui::TableEndRow(table);
2972
2973 StartPosY = window->DC.CursorPos.y;
2974 ItemsHeight = items_height;
2975 ItemsCount = items_count;
2976 DisplayStart = -1;
2977 DisplayEnd = 0;
2978
2979 // Acquire temporary buffer
2980 if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
2981 g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
2982 ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
2983 data->Reset(this);
2984 data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
2985 TempData = data;
2986}
2987
2988void ImGuiListClipper::End()
2989{
2990 if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
2991 {
2992 // 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.
2993 ImGuiContext& g = *Ctx;
2994 IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
2995 if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
2996 ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
2997
2998 // Restore temporary buffer and fix back pointers which may be invalidated when nesting
2999 IM_ASSERT(data->ListClipper == this);
3000 data->StepNo = data->Ranges.Size;
3001 if (--g.ClipperTempDataStacked > 0)
3002 {
3003 data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3004 data->ListClipper->TempData = data;
3005 }
3006 TempData = NULL;
3007 }
3008 ItemsCount = -1;
3009}
3010
3011void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
3012{
3013 ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
3014 IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
3015 IM_ASSERT(item_begin <= item_end);
3016 if (item_begin < item_end)
3017 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
3018}
3019
3020static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
3021{
3022 ImGuiContext& g = *clipper->Ctx;
3023 ImGuiWindow* window = g.CurrentWindow;
3024 ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
3025 IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
3026
3027 ImGuiTable* table = g.CurrentTable;
3028 if (table && table->IsInsideRow)
3029 ImGui::TableEndRow(table);
3030
3031 // No items
3032 if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
3033 return false;
3034
3035 // While we are in frozen row state, keep displaying items one by one, unclipped
3036 // FIXME: Could be stored as a table-agnostic state.
3037 if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
3038 {
3039 clipper->DisplayStart = data->ItemsFrozen;
3040 clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
3041 if (clipper->DisplayStart < clipper->DisplayEnd)
3042 data->ItemsFrozen++;
3043 return true;
3044 }
3045
3046 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
3047 bool calc_clipping = false;
3048 if (data->StepNo == 0)
3049 {
3050 clipper->StartPosY = window->DC.CursorPos.y;
3051 if (clipper->ItemsHeight <= 0.0f)
3052 {
3053 // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
3054 data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
3055 clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
3056 clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
3057 data->StepNo = 1;
3058 return true;
3059 }
3060 calc_clipping = true; // If on the first step with known item height, calculate clipping.
3061 }
3062
3063 // Step 1: Let the clipper infer height from first range
3064 if (clipper->ItemsHeight <= 0.0f)
3065 {
3066 IM_ASSERT(data->StepNo == 1);
3067 if (table)
3068 IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
3069
3070 clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
3071 bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
3072 if (affected_by_floating_point_precision)
3073 clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
3074
3075 IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
3076 calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
3077 }
3078
3079 // Step 0 or 1: Calculate the actual ranges of visible elements.
3080 const int already_submitted = clipper->DisplayEnd;
3081 if (calc_clipping)
3082 {
3083 if (g.LogEnabled)
3084 {
3085 // If logging is active, do not perform any clipping
3086 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
3087 }
3088 else
3089 {
3090 // Add range selected to be included for navigation
3091 const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
3092 if (is_nav_request)
3093 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
3094 if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
3095 data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
3096
3097 // Add focused/active item
3098 ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
3099 if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
3100 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
3101
3102 // Add visible range
3103 const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
3104 const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
3105 data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max));
3106 }
3107
3108 // Convert position ranges to item index ranges
3109 // - 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.
3110 // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
3111 // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
3112 for (ImGuiListClipperRange& range : data->Ranges)
3113 if (range.PosToIndexConvert)
3114 {
3115 int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
3116 int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
3117 range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
3118 range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
3119 range.PosToIndexConvert = false;
3120 }
3121 ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
3122 }
3123
3124 // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
3125 while (data->StepNo < data->Ranges.Size)
3126 {
3127 clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
3128 clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
3129 if (clipper->DisplayStart > already_submitted) //-V1051
3130 ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart);
3131 data->StepNo++;
3132 if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
3133 continue;
3134 return true;
3135 }
3136
3137 // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
3138 // Advance the cursor to the end of the list and then returns 'false' to end the loop.
3139 if (clipper->ItemsCount < INT_MAX)
3140 ImGuiListClipper_SeekCursorForItem(clipper, clipper->ItemsCount);
3141
3142 return false;
3143}
3144
3145bool ImGuiListClipper::Step()
3146{
3147 ImGuiContext& g = *Ctx;
3148 bool need_items_height = (ItemsHeight <= 0.0f);
3149 bool ret = ImGuiListClipper_StepInternal(this);
3150 if (ret && (DisplayStart == DisplayEnd))
3151 ret = false;
3152 if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
3153 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
3154 if (need_items_height && ItemsHeight > 0.0f)
3155 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
3156 if (ret)
3157 {
3158 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
3159 }
3160 else
3161 {
3162 IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
3163 End();
3164 }
3165 return ret;
3166}
3167
3168//-----------------------------------------------------------------------------
3169// [SECTION] STYLING
3170//-----------------------------------------------------------------------------
3171
3172ImGuiStyle& ImGui::GetStyle()
3173{
3174 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3175 return GImGui->Style;
3176}
3177
3178ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
3179{
3180 ImGuiStyle& style = GImGui->Style;
3181 ImVec4 c = style.Colors[idx];
3182 c.w *= style.Alpha * alpha_mul;
3183 return ColorConvertFloat4ToU32(c);
3184}
3185
3186ImU32 ImGui::GetColorU32(const ImVec4& col)
3187{
3188 ImGuiStyle& style = GImGui->Style;
3189 ImVec4 c = col;
3190 c.w *= style.Alpha;
3191 return ColorConvertFloat4ToU32(c);
3192}
3193
3194const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
3195{
3196 ImGuiStyle& style = GImGui->Style;
3197 return style.Colors[idx];
3198}
3199
3200ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
3201{
3202 ImGuiStyle& style = GImGui->Style;
3203 alpha_mul *= style.Alpha;
3204 if (alpha_mul >= 1.0f)
3205 return col;
3206 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
3207 a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
3208 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
3209}
3210
3211// 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
3212void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
3213{
3214 ImGuiContext& g = *GImGui;
3215 ImGuiColorMod backup;
3216 backup.Col = idx;
3217 backup.BackupValue = g.Style.Colors[idx];
3218 g.ColorStack.push_back(backup);
3219 if (g.DebugFlashStyleColorIdx != idx)
3220 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
3221}
3222
3223void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
3224{
3225 ImGuiContext& g = *GImGui;
3226 ImGuiColorMod backup;
3227 backup.Col = idx;
3228 backup.BackupValue = g.Style.Colors[idx];
3229 g.ColorStack.push_back(backup);
3230 if (g.DebugFlashStyleColorIdx != idx)
3231 g.Style.Colors[idx] = col;
3232}
3233
3234void ImGui::PopStyleColor(int count)
3235{
3236 ImGuiContext& g = *GImGui;
3237 if (g.ColorStack.Size < count)
3238 {
3239 IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!");
3240 count = g.ColorStack.Size;
3241 }
3242 while (count > 0)
3243 {
3244 ImGuiColorMod& backup = g.ColorStack.back();
3245 g.Style.Colors[backup.Col] = backup.BackupValue;
3246 g.ColorStack.pop_back();
3247 count--;
3248 }
3249}
3250
3251static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] =
3252{
3253 ImGuiCol_Text, ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive
3254};
3255
3256static const ImGuiDataVarInfo GStyleVarInfo[] =
3257{
3258 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
3259 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
3260 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
3261 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
3262 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
3263 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
3264 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
3265 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
3266 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
3267 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
3268 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
3269 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
3270 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
3271 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
3272 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
3273 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
3274 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
3275 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
3276 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
3277 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
3278 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
3279 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3280 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
3281 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
3282 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
3283 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
3284 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3285 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
3286 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
3287 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
3288 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
3289 { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
3290 { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) }, // ImGuiStyleVar_DockingSeparatorSize
3291};
3292
3293const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
3294{
3295 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
3296 IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
3297 return &GStyleVarInfo[idx];
3298}
3299
3300void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
3301{
3302 ImGuiContext& g = *GImGui;
3303 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3304 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
3305 {
3306 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
3307 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3308 *pvar = val;
3309 return;
3310 }
3311 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3312}
3313
3314void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
3315{
3316 ImGuiContext& g = *GImGui;
3317 const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);
3318 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
3319 {
3320 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3321 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3322 *pvar = val;
3323 return;
3324 }
3325 IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3326}
3327
3328void ImGui::PopStyleVar(int count)
3329{
3330 ImGuiContext& g = *GImGui;
3331 if (g.StyleVarStack.Size < count)
3332 {
3333 IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!");
3334 count = g.StyleVarStack.Size;
3335 }
3336 while (count > 0)
3337 {
3338 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
3339 ImGuiStyleMod& backup = g.StyleVarStack.back();
3340 const ImGuiDataVarInfo* info = GetStyleVarInfo(backup.VarIdx);
3341 void* data = info->GetVarPtr(&g.Style);
3342 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
3343 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
3344 g.StyleVarStack.pop_back();
3345 count--;
3346 }
3347}
3348
3349const char* ImGui::GetStyleColorName(ImGuiCol idx)
3350{
3351 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
3352 switch (idx)
3353 {
3354 case ImGuiCol_Text: return "Text";
3355 case ImGuiCol_TextDisabled: return "TextDisabled";
3356 case ImGuiCol_WindowBg: return "WindowBg";
3357 case ImGuiCol_ChildBg: return "ChildBg";
3358 case ImGuiCol_PopupBg: return "PopupBg";
3359 case ImGuiCol_Border: return "Border";
3360 case ImGuiCol_BorderShadow: return "BorderShadow";
3361 case ImGuiCol_FrameBg: return "FrameBg";
3362 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
3363 case ImGuiCol_FrameBgActive: return "FrameBgActive";
3364 case ImGuiCol_TitleBg: return "TitleBg";
3365 case ImGuiCol_TitleBgActive: return "TitleBgActive";
3366 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
3367 case ImGuiCol_MenuBarBg: return "MenuBarBg";
3368 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
3369 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
3370 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
3371 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
3372 case ImGuiCol_CheckMark: return "CheckMark";
3373 case ImGuiCol_SliderGrab: return "SliderGrab";
3374 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
3375 case ImGuiCol_Button: return "Button";
3376 case ImGuiCol_ButtonHovered: return "ButtonHovered";
3377 case ImGuiCol_ButtonActive: return "ButtonActive";
3378 case ImGuiCol_Header: return "Header";
3379 case ImGuiCol_HeaderHovered: return "HeaderHovered";
3380 case ImGuiCol_HeaderActive: return "HeaderActive";
3381 case ImGuiCol_Separator: return "Separator";
3382 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
3383 case ImGuiCol_SeparatorActive: return "SeparatorActive";
3384 case ImGuiCol_ResizeGrip: return "ResizeGrip";
3385 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
3386 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
3387 case ImGuiCol_Tab: return "Tab";
3388 case ImGuiCol_TabHovered: return "TabHovered";
3389 case ImGuiCol_TabActive: return "TabActive";
3390 case ImGuiCol_TabUnfocused: return "TabUnfocused";
3391 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
3392 case ImGuiCol_DockingPreview: return "DockingPreview";
3393 case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg";
3394 case ImGuiCol_PlotLines: return "PlotLines";
3395 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
3396 case ImGuiCol_PlotHistogram: return "PlotHistogram";
3397 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
3398 case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
3399 case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
3400 case ImGuiCol_TableBorderLight: return "TableBorderLight";
3401 case ImGuiCol_TableRowBg: return "TableRowBg";
3402 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
3403 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3404 case ImGuiCol_DragDropTarget: return "DragDropTarget";
3405 case ImGuiCol_NavHighlight: return "NavHighlight";
3406 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
3407 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
3408 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
3409 }
3410 IM_ASSERT(0);
3411 return "Unknown";
3412}
3413
3414
3415//-----------------------------------------------------------------------------
3416// [SECTION] RENDER HELPERS
3417// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
3418// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
3419// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
3420//-----------------------------------------------------------------------------
3421
3422const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
3423{
3424 const char* text_display_end = text;
3425 if (!text_end)
3426 text_end = (const char*)-1;
3427
3428 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
3429 text_display_end++;
3430 return text_display_end;
3431}
3432
3433// Internal ImGui functions to render text
3434// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3435void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3436{
3437 ImGuiContext& g = *GImGui;
3438 ImGuiWindow* window = g.CurrentWindow;
3439
3440 // Hide anything after a '##' string
3441 const char* text_display_end;
3442 if (hide_text_after_hash)
3443 {
3444 text_display_end = FindRenderedTextEnd(text, text_end);
3445 }
3446 else
3447 {
3448 if (!text_end)
3449 text_end = text + strlen(text); // FIXME-OPT
3450 text_display_end = text_end;
3451 }
3452
3453 if (text != text_display_end)
3454 {
3455 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3456 if (g.LogEnabled)
3457 LogRenderedText(&pos, text, text_display_end);
3458 }
3459}
3460
3461void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3462{
3463 ImGuiContext& g = *GImGui;
3464 ImGuiWindow* window = g.CurrentWindow;
3465
3466 if (!text_end)
3467 text_end = text + strlen(text); // FIXME-OPT
3468
3469 if (text != text_end)
3470 {
3471 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3472 if (g.LogEnabled)
3473 LogRenderedText(&pos, text, text_end);
3474 }
3475}
3476
3477// Default clip_rect uses (pos_min,pos_max)
3478// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3479// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList.
3480// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
3481// better advantage of the render function taking size into account for coarse clipping.
3482void 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)
3483{
3484 // Perform CPU side clipping for single clipped element to avoid using scissor state
3485 ImVec2 pos = pos_min;
3486 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3487
3488 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3489 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3490 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3491 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3492 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3493
3494 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3495 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3496 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3497
3498 // Render
3499 if (need_clipping)
3500 {
3501 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3502 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3503 }
3504 else
3505 {
3506 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3507 }
3508}
3509
3510void 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)
3511{
3512 // Hide anything after a '##' string
3513 const char* text_display_end = FindRenderedTextEnd(text, text_end);
3514 const int text_len = (int)(text_display_end - text);
3515 if (text_len == 0)
3516 return;
3517
3518 ImGuiContext& g = *GImGui;
3519 ImGuiWindow* window = g.CurrentWindow;
3520 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
3521 if (g.LogEnabled)
3522 LogRenderedText(&pos_min, text, text_display_end);
3523}
3524
3525// Another overly complex function until we reorganize everything into a nice all-in-one helper.
3526// 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.
3527// 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.
3528void 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)
3529{
3530 ImGuiContext& g = *GImGui;
3531 if (text_end_full == NULL)
3532 text_end_full = FindRenderedTextEnd(text);
3533 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
3534
3535 //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));
3536 //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));
3537 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
3538 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
3539 if (text_size.x > pos_max.x - pos_min.x)
3540 {
3541 // Hello wo...
3542 // | | |
3543 // min max ellipsis_max
3544 // <-> this is generally some padding value
3545
3546 const ImFont* font = draw_list->_Data->Font;
3547 const float font_size = draw_list->_Data->FontSize;
3548 const float font_scale = font_size / font->FontSize;
3549 const char* text_end_ellipsis = NULL;
3550 const float ellipsis_width = font->EllipsisWidth * font_scale;
3551
3552 // We can now claim the space between pos_max.x and ellipsis_max.x
3553 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
3554 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
3555 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
3556 {
3557 // Always display at least 1 character if there's no room for character + ellipsis
3558 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
3559 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
3560 }
3561 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
3562 {
3563 // 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)
3564 text_end_ellipsis--;
3565 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
3566 }
3567
3568 // Render text, render ellipsis
3569 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
3570 ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
3571 if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
3572 for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
3573 font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
3574 }
3575 else
3576 {
3577 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
3578 }
3579
3580 if (g.LogEnabled)
3581 LogRenderedText(&pos_min, text, text_end_full);
3582}
3583
3584// Render a rectangle shaped with optional rounding and borders
3585void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
3586{
3587 ImGuiContext& g = *GImGui;
3588 ImGuiWindow* window = g.CurrentWindow;
3589 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3590 const float border_size = g.Style.FrameBorderSize;
3591 if (border && border_size > 0.0f)
3592 {
3593 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3594 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3595 }
3596}
3597
3598void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3599{
3600 ImGuiContext& g = *GImGui;
3601 ImGuiWindow* window = g.CurrentWindow;
3602 const float border_size = g.Style.FrameBorderSize;
3603 if (border_size > 0.0f)
3604 {
3605 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3606 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3607 }
3608}
3609
3610void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
3611{
3612 ImGuiContext& g = *GImGui;
3613 if (id != g.NavId)
3614 return;
3615 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
3616 return;
3617 ImGuiWindow* window = g.CurrentWindow;
3618 if (window->DC.NavHideHighlightOneFrame)
3619 return;
3620
3621 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
3622 ImRect display_rect = bb;
3623 display_rect.ClipWith(window->ClipRect);
3624 const float thickness = 2.0f;
3625 if (flags & ImGuiNavHighlightFlags_Compact)
3626 {
3627 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
3628 }
3629 else
3630 {
3631 const float distance = 3.0f + thickness * 0.5f;
3632 display_rect.Expand(ImVec2(distance, distance));
3633 bool fully_visible = window->ClipRect.Contains(display_rect);
3634 if (!fully_visible)
3635 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
3636 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness);
3637 if (!fully_visible)
3638 window->DrawList->PopClipRect();
3639 }
3640}
3641
3642void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
3643{
3644 ImGuiContext& g = *GImGui;
3645 IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);
3646 ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;
3647 for (ImGuiViewportP* viewport : g.Viewports)
3648 {
3649 // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
3650 ImVec2 offset, size, uv[4];
3651 if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
3652 continue;
3653 const ImVec2 pos = base_pos - offset;
3654 const float scale = base_scale * viewport->DpiScale;
3655 if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
3656 continue;
3657 ImDrawList* draw_list = GetForegroundDrawList(viewport);
3658 ImTextureID tex_id = font_atlas->TexID;
3659 draw_list->PushTextureID(tex_id);
3660 draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
3661 draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
3662 draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border);
3663 draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill);
3664 draw_list->PopTextureID();
3665 }
3666}
3667
3668//-----------------------------------------------------------------------------
3669// [SECTION] INITIALIZATION, SHUTDOWN
3670//-----------------------------------------------------------------------------
3671
3672// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3673// 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
3674ImGuiContext* ImGui::GetCurrentContext()
3675{
3676 return GImGui;
3677}
3678
3679void ImGui::SetCurrentContext(ImGuiContext* ctx)
3680{
3681#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3682 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3683#else
3684 GImGui = ctx;
3685#endif
3686}
3687
3688void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3689{
3690 GImAllocatorAllocFunc = alloc_func;
3691 GImAllocatorFreeFunc = free_func;
3692 GImAllocatorUserData = user_data;
3693}
3694
3695// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
3696void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3697{
3698 *p_alloc_func = GImAllocatorAllocFunc;
3699 *p_free_func = GImAllocatorFreeFunc;
3700 *p_user_data = GImAllocatorUserData;
3701}
3702
3703ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3704{
3705 ImGuiContext* prev_ctx = GetCurrentContext();
3706 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3707 SetCurrentContext(ctx);
3708 Initialize();
3709 if (prev_ctx != NULL)
3710 SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
3711 return ctx;
3712}
3713
3714void ImGui::DestroyContext(ImGuiContext* ctx)
3715{
3716 ImGuiContext* prev_ctx = GetCurrentContext();
3717 if (ctx == NULL) //-V1051
3718 ctx = prev_ctx;
3719 SetCurrentContext(ctx);
3720 Shutdown();
3721 SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
3722 IM_DELETE(ctx);
3723}
3724
3725// IMPORTANT: ###xxx suffixes must be same in ALL languages
3726static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
3727{
3728 { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
3729 { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
3730 { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
3731 { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
3732 { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" },
3733 { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" },
3734 { ImGuiLocKey_WindowingPopup, "(Popup)" },
3735 { ImGuiLocKey_WindowingUntitled, "(Untitled)" },
3736 { ImGuiLocKey_DockingHideTabBar, "Hide tab bar###HideTabBar" },
3737 { ImGuiLocKey_DockingHoldShiftToDock, "Hold SHIFT to enable Docking window." },
3738 { ImGuiLocKey_DockingDragToUndockOrMoveNode,"Click and drag to move or undock whole node." },
3739};
3740
3741void ImGui::Initialize()
3742{
3743 ImGuiContext& g = *GImGui;
3744 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3745
3746 // Add .ini handle for ImGuiWindow and ImGuiTable types
3747 {
3748 ImGuiSettingsHandler ini_handler;
3749 ini_handler.TypeName = "Window";
3750 ini_handler.TypeHash = ImHashStr("Window");
3751 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
3752 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
3753 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
3754 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
3755 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
3756 AddSettingsHandler(&ini_handler);
3757 }
3758 TableSettingsAddSettingsHandler();
3759
3760 // Setup default localization table
3761 LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
3762
3763 // Setup default platform clipboard/IME handlers.
3764 g.IO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
3765 g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
3766 g.IO.ClipboardUserData = (void*)&g; // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function)
3767 g.IO.SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl;
3768
3769 // Create default viewport
3770 ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
3771 viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
3772 viewport->Idx = 0;
3773 viewport->PlatformWindowCreated = true;
3774 viewport->Flags = ImGuiViewportFlags_OwnedByApp;
3775 g.Viewports.push_back(viewport);
3776 g.TempBuffer.resize(1024 * 3 + 1, 0);
3777 g.ViewportCreatedCount++;
3778 g.PlatformIO.Viewports.push_back(g.Viewports[0]);
3779
3780 // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
3781 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
3782 if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
3783 || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
3784 || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
3785 || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
3786 g.KeysMayBeCharInput.SetBit(key);
3787
3788#ifdef IMGUI_HAS_DOCK
3789 // Initialize Docking
3790 DockContextInitialize(&g);
3791#endif
3792
3793 g.Initialized = true;
3794}
3795
3796// This function is merely here to free heap allocations.
3797void ImGui::Shutdown()
3798{
3799 ImGuiContext& g = *GImGui;
3800 IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
3801 IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
3802
3803 // 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)
3804 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3805 {
3806 g.IO.Fonts->Locked = false;
3807 IM_DELETE(g.IO.Fonts);
3808 }
3809 g.IO.Fonts = NULL;
3810 g.DrawListSharedData.TempBuffer.clear();
3811
3812 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3813 if (!g.Initialized)
3814 return;
3815
3816 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3817 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3818 SaveIniSettingsToDisk(g.IO.IniFilename);
3819
3820 // Destroy platform windows
3821 DestroyPlatformWindows();
3822
3823 // Shutdown extensions
3824 DockContextShutdown(&g);
3825
3826 CallContextHooks(&g, ImGuiContextHookType_Shutdown);
3827
3828 // Clear everything else
3829 g.Windows.clear_delete();
3830 g.WindowsFocusOrder.clear();
3831 g.WindowsTempSortBuffer.clear();
3832 g.CurrentWindow = NULL;
3833 g.CurrentWindowStack.clear();
3834 g.WindowsById.Clear();
3835 g.NavWindow = NULL;
3836 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
3837 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3838 g.MovingWindow = NULL;
3839
3840 g.KeysRoutingTable.Clear();
3841
3842 g.ColorStack.clear();
3843 g.StyleVarStack.clear();
3844 g.FontStack.clear();
3845 g.OpenPopupStack.clear();
3846 g.BeginPopupStack.clear();
3847 g.NavTreeNodeStack.clear();
3848
3849 g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL;
3850 g.Viewports.clear_delete();
3851
3852 g.TabBars.Clear();
3853 g.CurrentTabBarStack.clear();
3854 g.ShrinkWidthBuffer.clear();
3855
3856 g.ClipperTempData.clear_destruct();
3857
3858 g.Tables.Clear();
3859 g.TablesTempData.clear_destruct();
3860 g.DrawChannelsTempMergeBuffer.clear();
3861
3862 g.ClipboardHandlerData.clear();
3863 g.MenusIdSubmittedThisFrame.clear();
3864 g.InputTextState.ClearFreeMemory();
3865 g.InputTextDeactivatedState.ClearFreeMemory();
3866
3867 g.SettingsWindows.clear();
3868 g.SettingsHandlers.clear();
3869
3870 if (g.LogFile)
3871 {
3872#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
3873 if (g.LogFile != stdout)
3874#endif
3875 ImFileClose(g.LogFile);
3876 g.LogFile = NULL;
3877 }
3878 g.LogBuffer.clear();
3879 g.DebugLogBuf.clear();
3880 g.DebugLogIndex.clear();
3881
3882 g.Initialized = false;
3883}
3884
3885// No specific ordering/dependency support, will see as needed
3886ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3887{
3888 ImGuiContext& g = *ctx;
3889 IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
3890 g.Hooks.push_back(*hook);
3891 g.Hooks.back().HookId = ++g.HookIdNext;
3892 return g.HookIdNext;
3893}
3894
3895// Deferred removal, avoiding issue with changing vector while iterating it
3896void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
3897{
3898 ImGuiContext& g = *ctx;
3899 IM_ASSERT(hook_id != 0);
3900 for (ImGuiContextHook& hook : g.Hooks)
3901 if (hook.HookId == hook_id)
3902 hook.Type = ImGuiContextHookType_PendingRemoval_;
3903}
3904
3905// Call context hooks (used by e.g. test engine)
3906// We assume a small number of hooks so all stored in same array
3907void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3908{
3909 ImGuiContext& g = *ctx;
3910 for (ImGuiContextHook& hook : g.Hooks)
3911 if (hook.Type == hook_type)
3912 hook.Callback(&g, &hook);
3913}
3914
3915
3916//-----------------------------------------------------------------------------
3917// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
3918//-----------------------------------------------------------------------------
3919
3920// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
3921ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
3922{
3923 memset(this, 0, sizeof(*this));
3924 Ctx = ctx;
3925 Name = ImStrdup(name);
3926 NameBufLen = (int)strlen(name) + 1;
3927 ID = ImHashStr(name);
3928 IDStack.push_back(ID);
3929 ViewportAllowPlatformMonitorExtend = -1;
3930 ViewportPos = ImVec2(FLT_MAX, FLT_MAX);
3931 MoveId = GetID("#MOVE");
3932 TabId = GetID("#TAB");
3933 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
3934 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
3935 AutoFitFramesX = AutoFitFramesY = -1;
3936 AutoPosLastDirection = ImGuiDir_None;
3937 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0;
3938 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
3939 LastFrameActive = -1;
3940 LastFrameJustFocused = -1;
3941 LastTimeActive = -1.0f;
3942 FontWindowScale = FontDpiScale = 1.0f;
3943 SettingsOffset = -1;
3944 DockOrder = -1;
3945 DrawList = &DrawListInst;
3946 DrawList->_Data = &Ctx->DrawListSharedData;
3947 DrawList->_OwnerName = Name;
3948 NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
3949 IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass();
3950}
3951
3952ImGuiWindow::~ImGuiWindow()
3953{
3954 IM_ASSERT(DrawList == &DrawListInst);
3955 IM_DELETE(Name);
3956 ColumnsStorage.clear_destruct();
3957}
3958
3959static void SetCurrentWindow(ImGuiWindow* window)
3960{
3961 ImGuiContext& g = *GImGui;
3962 g.CurrentWindow = window;
3963 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
3964 if (window)
3965 {
3966 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
3967 ImGui::NavUpdateCurrentWindowIsScrollPushableX();
3968 }
3969}
3970
3971void ImGui::GcCompactTransientMiscBuffers()
3972{
3973 ImGuiContext& g = *GImGui;
3974 g.ItemFlagsStack.clear();
3975 g.GroupStack.clear();
3976 TableGcCompactSettings();
3977}
3978
3979// Free up/compact internal window buffers, we can use this when a window becomes unused.
3980// Not freed:
3981// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
3982// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
3983void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
3984{
3985 window->MemoryCompacted = true;
3986 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
3987 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
3988 window->IDStack.clear();
3989 window->DrawList->_ClearFreeMemory();
3990 window->DC.ChildWindows.clear();
3991 window->DC.ItemWidthStack.clear();
3992 window->DC.TextWrapPosStack.clear();
3993}
3994
3995void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
3996{
3997 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
3998 // The other buffers tends to amortize much faster.
3999 window->MemoryCompacted = false;
4000 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
4001 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
4002 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
4003}
4004
4005void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
4006{
4007 ImGuiContext& g = *GImGui;
4008
4009 // Clear previous active id
4010 if (g.ActiveId != 0)
4011 {
4012 // While most behaved code would make an effort to not steal active id during window move/drag operations,
4013 // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
4014 // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
4015 if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
4016 {
4017 IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
4018 g.MovingWindow = NULL;
4019 }
4020
4021 // This could be written in a more general way (e.g associate a hook to ActiveId),
4022 // but since this is currently quite an exception we'll leave it as is.
4023 // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()
4024 if (g.InputTextState.ID == g.ActiveId)
4025 InputTextDeactivateHook(g.ActiveId);
4026 }
4027
4028 // Set active id
4029 g.ActiveIdIsJustActivated = (g.ActiveId != id);
4030 if (g.ActiveIdIsJustActivated)
4031 {
4032 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 : "");
4033 g.ActiveIdTimer = 0.0f;
4034 g.ActiveIdHasBeenPressedBefore = false;
4035 g.ActiveIdHasBeenEditedBefore = false;
4036 g.ActiveIdMouseButton = -1;
4037 if (id != 0)
4038 {
4039 g.LastActiveId = id;
4040 g.LastActiveIdTimer = 0.0f;
4041 }
4042 }
4043 g.ActiveId = id;
4044 g.ActiveIdAllowOverlap = false;
4045 g.ActiveIdNoClearOnFocusLoss = false;
4046 g.ActiveIdWindow = window;
4047 g.ActiveIdHasBeenEditedThisFrame = false;
4048 g.ActiveIdFromShortcut = false;
4049 if (id)
4050 {
4051 g.ActiveIdIsAlive = id;
4052 g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
4053 IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
4054 }
4055
4056 // Clear declaration of inputs claimed by the widget
4057 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
4058 g.ActiveIdUsingNavDirMask = 0x00;
4059 g.ActiveIdUsingAllKeyboardKeys = false;
4060#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
4061 g.ActiveIdUsingNavInputMask = 0x00;
4062#endif
4063}
4064
4065void ImGui::ClearActiveID()
4066{
4067 SetActiveID(0, NULL); // g.ActiveId = 0;
4068}
4069
4070void ImGui::SetHoveredID(ImGuiID id)
4071{
4072 ImGuiContext& g = *GImGui;
4073 g.HoveredId = id;
4074 g.HoveredIdAllowOverlap = false;
4075 if (id != 0 && g.HoveredIdPreviousFrame != id)
4076 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
4077}
4078
4079ImGuiID ImGui::GetHoveredID()
4080{
4081 ImGuiContext& g = *GImGui;
4082 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
4083}
4084
4085void ImGui::MarkItemEdited(ImGuiID id)
4086{
4087 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
4088 // 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.
4089 ImGuiContext& g = *GImGui;
4090 if (g.LockMarkEdited > 0)
4091 return;
4092 if (g.ActiveId == id || g.ActiveId == 0)
4093 {
4094 g.ActiveIdHasBeenEditedThisFrame = true;
4095 g.ActiveIdHasBeenEditedBefore = true;
4096 }
4097
4098 // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
4099 // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
4100 IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id);
4101
4102 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
4103 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
4104}
4105
4106bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
4107{
4108 // An active popup disable hovering on other windows (apart from its own children)
4109 // FIXME-OPT: This could be cached/stored within the window.
4110 ImGuiContext& g = *GImGui;
4111 if (g.NavWindow)
4112 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree)
4113 if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree)
4114 {
4115 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
4116 // NB: The 'else' is important because Modal windows are also Popups.
4117 bool want_inhibit = false;
4118 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
4119 want_inhibit = true;
4120 else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
4121 want_inhibit = true;
4122
4123 // Inhibit hover unless the window is within the stack of our modal/popup
4124 if (want_inhibit)
4125 if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
4126 return false;
4127 }
4128
4129 // Filter by viewport
4130 if (window->Viewport != g.MouseViewport)
4131 if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)
4132 return false;
4133
4134 return true;
4135}
4136
4137static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
4138{
4139 ImGuiContext& g = *GImGui;
4140 if (flags & ImGuiHoveredFlags_DelayNormal)
4141 return g.Style.HoverDelayNormal;
4142 if (flags & ImGuiHoveredFlags_DelayShort)
4143 return g.Style.HoverDelayShort;
4144 return 0.0f;
4145}
4146
4147static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
4148{
4149 // Allow instance flags to override shared flags
4150 if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
4151 shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
4152 return user_flags | shared_flags;
4153}
4154
4155// This is roughly matching the behavior of internal-facing ItemHoverable()
4156// - 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()
4157// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
4158bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
4159{
4160 ImGuiContext& g = *GImGui;
4161 ImGuiWindow* window = g.CurrentWindow;
4162 IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!");
4163
4164 if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
4165 {
4166 if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4167 return false;
4168 if (!IsItemFocused())
4169 return false;
4170
4171 if (flags & ImGuiHoveredFlags_ForTooltip)
4172 flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
4173 }
4174 else
4175 {
4176 // Test for bounding box overlap, as updated as ItemAdd()
4177 ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
4178 if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
4179 return false;
4180
4181 if (flags & ImGuiHoveredFlags_ForTooltip)
4182 flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
4183
4184 IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy)) == 0); // Flags not supported by this function
4185
4186 // Done with rectangle culling so we can perform heavier checks now
4187 // Test if we are hovering the right window (our window could be behind another window)
4188 // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
4189 // [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
4190 // 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
4191 // the test that has been running for a long while.
4192 if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
4193 if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
4194 return false;
4195
4196 // Test if another item is active (e.g. being dragged)
4197 const ImGuiID id = g.LastItemData.ID;
4198 if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
4199 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4200 if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId)
4201 return false;
4202
4203 // Test if interactions on this window are blocked by an active popup or modal.
4204 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
4205 if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.InFlags & ImGuiItemFlags_NoWindowHoverableCheck))
4206 return false;
4207
4208 // Test if the item is disabled
4209 if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4210 return false;
4211
4212 // Special handling for calling after Begin() which represent the title bar or tab.
4213 // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
4214 // will never be overwritten so we need to detect the case.
4215 if (id == window->MoveId && window->WriteAccessed)
4216 return false;
4217
4218 // Test if using AllowOverlap and overlapped
4219 if ((g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
4220 if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
4221 if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
4222 return false;
4223 }
4224
4225 // Handle hover delay
4226 // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
4227 const float delay = CalcDelayFromHoveredFlags(flags);
4228 if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
4229 {
4230 ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect);
4231 if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
4232 g.HoverItemDelayTimer = 0.0f;
4233 g.HoverItemDelayId = hover_delay_id;
4234
4235 // When changing hovered item we requires a bit of stationary delay before activating hover timer,
4236 // but once unlocked on a given item we also moving.
4237 //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); }
4238 if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
4239 return false;
4240
4241 if (g.HoverItemDelayTimer < delay)
4242 return false;
4243 }
4244
4245 return true;
4246}
4247
4248// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
4249// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
4250// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
4251// If you used this in your legacy/custom widgets code:
4252// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'.
4253// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
4254bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
4255{
4256 ImGuiContext& g = *GImGui;
4257 ImGuiWindow* window = g.CurrentWindow;
4258 if (g.HoveredWindow != window)
4259 return false;
4260 if (!IsMouseHoveringRect(bb.Min, bb.Max))
4261 return false;
4262
4263 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
4264 return false;
4265 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4266 if (!g.ActiveIdFromShortcut)
4267 return false;
4268
4269 // Done with rectangle culling so we can perform heavier checks now.
4270 if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
4271 {
4272 g.HoveredIdDisabled = true;
4273 return false;
4274 }
4275
4276 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
4277 // hover test in widgets code. We could also decide to split this function is two.
4278 if (id != 0)
4279 {
4280 // Drag source doesn't report as hovered
4281 if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
4282 return false;
4283
4284 SetHoveredID(id);
4285
4286 // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
4287 // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
4288 if (item_flags & ImGuiItemFlags_AllowOverlap)
4289 {
4290 g.HoveredIdAllowOverlap = true;
4291 if (g.HoveredIdPreviousFrame != id)
4292 return false;
4293 }
4294
4295 // Display shortcut (only works with mouse)
4296 if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut))
4297 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
4298 SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
4299 }
4300
4301 // When disabled we'll return false but still set HoveredId
4302 if (item_flags & ImGuiItemFlags_Disabled)
4303 {
4304 // Release active id if turning disabled
4305 if (g.ActiveId == id && id != 0)
4306 ClearActiveID();
4307 g.HoveredIdDisabled = true;
4308 return false;
4309 }
4310
4311#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4312 if (id != 0)
4313 {
4314 // [DEBUG] Item Picker tool!
4315 // We perform the check here because reaching is path is rare (1~ time a frame),
4316 // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
4317 // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
4318 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
4319 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
4320 if (g.DebugItemPickerBreakId == id)
4321 IM_DEBUG_BREAK();
4322 }
4323#endif
4324
4325 if (g.NavDisableMouseHover)
4326 return false;
4327
4328 return true;
4329}
4330
4331// FIXME: This is inlined/duplicated in ItemAdd()
4332// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
4333bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
4334{
4335 ImGuiContext& g = *GImGui;
4336 ImGuiWindow* window = g.CurrentWindow;
4337 if (!bb.Overlaps(window->ClipRect))
4338 if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
4339 if (!g.LogEnabled)
4340 return true;
4341 return false;
4342}
4343
4344// This is also inlined in ItemAdd()
4345// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
4346void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
4347{
4348 ImGuiContext& g = *GImGui;
4349 g.LastItemData.ID = item_id;
4350 g.LastItemData.InFlags = in_flags;
4351 g.LastItemData.StatusFlags = item_flags;
4352 g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
4353}
4354
4355float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
4356{
4357 if (wrap_pos_x < 0.0f)
4358 return 0.0f;
4359
4360 ImGuiContext& g = *GImGui;
4361 ImGuiWindow* window = g.CurrentWindow;
4362 if (wrap_pos_x == 0.0f)
4363 {
4364 // We could decide to setup a default wrapping max point for auto-resizing windows,
4365 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
4366 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
4367 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
4368 //else
4369 wrap_pos_x = window->WorkRect.Max.x;
4370 }
4371 else if (wrap_pos_x > 0.0f)
4372 {
4373 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
4374 }
4375
4376 return ImMax(wrap_pos_x - pos.x, 1.0f);
4377}
4378
4379// IM_ALLOC() == ImGui::MemAlloc()
4380void* ImGui::MemAlloc(size_t size)
4381{
4382 void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
4383#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4384 if (ImGuiContext* ctx = GImGui)
4385 DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
4386#endif
4387 return ptr;
4388}
4389
4390// IM_FREE() == ImGui::MemFree()
4391void ImGui::MemFree(void* ptr)
4392{
4393#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4394 if (ptr != NULL)
4395 if (ImGuiContext* ctx = GImGui)
4396 DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
4397#endif
4398 return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
4399}
4400
4401// 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"
4402void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
4403{
4404 ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4405 IM_UNUSED(ptr);
4406 if (entry->FrameCount != frame_count)
4407 {
4408 info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
4409 entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4410 entry->FrameCount = frame_count;
4411 entry->AllocCount = entry->FreeCount = 0;
4412 }
4413 if (size != (size_t)-1)
4414 {
4415 entry->AllocCount++;
4416 info->TotalAllocCount++;
4417 //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr);
4418 }
4419 else
4420 {
4421 entry->FreeCount++;
4422 info->TotalFreeCount++;
4423 //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
4424 }
4425}
4426
4427const char* ImGui::GetClipboardText()
4428{
4429 ImGuiContext& g = *GImGui;
4430 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
4431}
4432
4433void ImGui::SetClipboardText(const char* text)
4434{
4435 ImGuiContext& g = *GImGui;
4436 if (g.IO.SetClipboardTextFn)
4437 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
4438}
4439
4440const char* ImGui::GetVersion()
4441{
4442 return IMGUI_VERSION;
4443}
4444
4445ImGuiIO& ImGui::GetIO()
4446{
4447 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
4448 return GImGui->IO;
4449}
4450
4451ImGuiPlatformIO& ImGui::GetPlatformIO()
4452{
4453 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
4454 return GImGui->PlatformIO;
4455}
4456
4457// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
4458ImDrawData* ImGui::GetDrawData()
4459{
4460 ImGuiContext& g = *GImGui;
4461 ImGuiViewportP* viewport = g.Viewports[0];
4462 return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
4463}
4464
4465double ImGui::GetTime()
4466{
4467 return GImGui->Time;
4468}
4469
4470int ImGui::GetFrameCount()
4471{
4472 return GImGui->FrameCount;
4473}
4474
4475static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
4476{
4477 // Create the draw list on demand, because they are not frequently used for all viewports
4478 ImGuiContext& g = *GImGui;
4479 IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
4480 ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
4481 if (draw_list == NULL)
4482 {
4483 draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
4484 draw_list->_OwnerName = drawlist_name;
4485 viewport->BgFgDrawLists[drawlist_no] = draw_list;
4486 }
4487
4488 // Our ImDrawList system requires that there is always a command
4489 if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
4490 {
4491 draw_list->_ResetForNewFrame();
4492 draw_list->PushTextureID(g.IO.Fonts->TexID);
4493 draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
4494 viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
4495 }
4496 return draw_list;
4497}
4498
4499ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
4500{
4501 return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
4502}
4503
4504ImDrawList* ImGui::GetBackgroundDrawList()
4505{
4506 ImGuiContext& g = *GImGui;
4507 return GetBackgroundDrawList(g.CurrentWindow->Viewport);
4508}
4509
4510ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
4511{
4512 return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
4513}
4514
4515ImDrawList* ImGui::GetForegroundDrawList()
4516{
4517 ImGuiContext& g = *GImGui;
4518 return GetForegroundDrawList(g.CurrentWindow->Viewport);
4519}
4520
4521ImDrawListSharedData* ImGui::GetDrawListSharedData()
4522{
4523 return &GImGui->DrawListSharedData;
4524}
4525
4526void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
4527{
4528 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
4529 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
4530 // This is because we want ActiveId to be set even when the window is not permitted to move.
4531 ImGuiContext& g = *GImGui;
4532 FocusWindow(window);
4533 SetActiveID(window->MoveId, window);
4534 g.NavDisableHighlight = true;
4535 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos;
4536 g.ActiveIdNoClearOnFocusLoss = true;
4537 SetActiveIdUsingAllKeyboardKeys();
4538
4539 bool can_move_window = true;
4540 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove))
4541 can_move_window = false;
4542 if (ImGuiDockNode* node = window->DockNodeAsHost)
4543 if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove))
4544 can_move_window = false;
4545 if (can_move_window)
4546 g.MovingWindow = window;
4547}
4548
4549// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them.
4550void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock)
4551{
4552 ImGuiContext& g = *GImGui;
4553 bool can_undock_node = false;
4554 if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0)
4555 {
4556 // Can undock if:
4557 // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window)
4558 // - 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)
4559 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
4560 if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that.
4561 can_undock_node = true;
4562 }
4563
4564 const bool clicked = IsMouseClicked(0);
4565 const bool dragging = IsMouseDragging(0);
4566 if (can_undock_node && dragging)
4567 DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame
4568 else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window)
4569 StartMouseMovingWindow(window);
4570}
4571
4572// Handle mouse moving window
4573// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
4574// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
4575// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
4576// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
4577void ImGui::UpdateMouseMovingWindowNewFrame()
4578{
4579 ImGuiContext& g = *GImGui;
4580 if (g.MovingWindow != NULL)
4581 {
4582 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
4583 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
4584 KeepAliveID(g.ActiveId);
4585 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree);
4586 ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree;
4587
4588 // When a window stop being submitted while being dragged, it may will its viewport until next Begin()
4589 const bool window_disappared = (!moving_window->WasActive && !moving_window->Active);
4590 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared)
4591 {
4592 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
4593 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
4594 {
4595 SetWindowPos(moving_window, pos, ImGuiCond_Always);
4596 if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
4597 {
4598 moving_window->Viewport->Pos = pos;
4599 moving_window->Viewport->UpdateWorkRect();
4600 }
4601 }
4602 FocusWindow(g.MovingWindow);
4603 }
4604 else
4605 {
4606 if (!window_disappared)
4607 {
4608 // Try to merge the window back into the main viewport.
4609 // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports)
4610 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
4611 UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport);
4612
4613 // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button.
4614 if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted())
4615 g.MouseViewport = moving_window->Viewport;
4616
4617 // Clear the NoInput window flag set by the Viewport system
4618 if (moving_window->Viewport)
4619 moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs;
4620 }
4621
4622 g.MovingWindow = NULL;
4623 ClearActiveID();
4624 }
4625 }
4626 else
4627 {
4628 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
4629 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
4630 {
4631 KeepAliveID(g.ActiveId);
4632 if (!g.IO.MouseDown[0])
4633 ClearActiveID();
4634 }
4635 }
4636}
4637
4638// Initiate moving window when clicking on empty space or title bar.
4639// Handle left-click and right-click focus.
4640void ImGui::UpdateMouseMovingWindowEndFrame()
4641{
4642 ImGuiContext& g = *GImGui;
4643 if (g.ActiveId != 0 || g.HoveredId != 0)
4644 return;
4645
4646 // Unless we just made a window/popup appear
4647 if (g.NavWindow && g.NavWindow->Appearing)
4648 return;
4649
4650 // Click on empty space to focus window and start moving
4651 // (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!)
4652 if (g.IO.MouseClicked[0])
4653 {
4654 // Handle the edge case of a popup being closed while clicking in its empty space.
4655 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
4656 ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4657 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
4658
4659 if (root_window != NULL && !is_closed_popup)
4660 {
4661 StartMouseMovingWindow(g.HoveredWindow); //-V595
4662
4663 // Cancel moving if clicked outside of title bar
4664 if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
4665 if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive)
4666 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
4667 g.MovingWindow = NULL;
4668
4669 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
4670 if (g.HoveredIdDisabled)
4671 g.MovingWindow = NULL;
4672 }
4673 else if (root_window == NULL && g.NavWindow != NULL)
4674 {
4675 // Clicking on void disable focus
4676 FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
4677 }
4678 }
4679
4680 // With right mouse button we close popups without changing focus based on where the mouse is aimed
4681 // Instead, focus will be restored to the window under the bottom-most closed popup.
4682 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
4683 if (g.IO.MouseClicked[1])
4684 {
4685 // Find the top-most window between HoveredWindow and the top-most Modal Window.
4686 // This is where we can trim the popup stack.
4687 ImGuiWindow* modal = GetTopMostPopupModal();
4688 bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));
4689 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
4690 }
4691}
4692
4693// This is called during NewFrame()->UpdateViewportsNewFrame() only.
4694// Need to keep in sync with SetWindowPos()
4695static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta)
4696{
4697 window->Pos += delta;
4698 window->ClipRect.Translate(delta);
4699 window->OuterRectClipped.Translate(delta);
4700 window->InnerRect.Translate(delta);
4701 window->DC.CursorPos += delta;
4702 window->DC.CursorStartPos += delta;
4703 window->DC.CursorMaxPos += delta;
4704 window->DC.IdealMaxPos += delta;
4705}
4706
4707static void ScaleWindow(ImGuiWindow* window, float scale)
4708{
4709 ImVec2 origin = window->Viewport->Pos;
4710 window->Pos = ImFloor((window->Pos - origin) * scale + origin);
4711 window->Size = ImTrunc(window->Size * scale);
4712 window->SizeFull = ImTrunc(window->SizeFull * scale);
4713 window->ContentSize = ImTrunc(window->ContentSize * scale);
4714}
4715
4716static bool IsWindowActiveAndVisible(ImGuiWindow* window)
4717{
4718 return (window->Active) && (!window->Hidden);
4719}
4720
4721// 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)
4722void ImGui::UpdateHoveredWindowAndCaptureFlags()
4723{
4724 ImGuiContext& g = *GImGui;
4725 ImGuiIO& io = g.IO;
4726
4727 // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
4728 // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
4729 g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
4730
4731 // Find the window hovered by mouse:
4732 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
4733 // - 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.
4734 // - 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.
4735 bool clear_hovered_windows = false;
4736 FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
4737 IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
4738
4739 // Modal windows prevents mouse from hovering behind them.
4740 ImGuiWindow* modal_window = GetTopMostPopupModal();
4741 if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ?
4742 clear_hovered_windows = true;
4743
4744 // Disabled mouse?
4745 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
4746 clear_hovered_windows = true;
4747
4748 // We track click ownership. When clicked outside of a window the click is owned by the application and
4749 // won't report hovering nor request capture even while dragging over our windows afterward.
4750 const bool has_open_popup = (g.OpenPopupStack.Size > 0);
4751 const bool has_open_modal = (modal_window != NULL);
4752 int mouse_earliest_down = -1;
4753 bool mouse_any_down = false;
4754 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
4755 {
4756 if (io.MouseClicked[i])
4757 {
4758 io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
4759 io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
4760 }
4761 mouse_any_down |= io.MouseDown[i];
4762 if (io.MouseDown[i])
4763 if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
4764 mouse_earliest_down = i;
4765 }
4766 const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
4767 const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
4768
4769 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
4770 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
4771 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
4772 if (!mouse_avail && !mouse_dragging_extern_payload)
4773 clear_hovered_windows = true;
4774
4775 if (clear_hovered_windows)
4776 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4777
4778 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
4779 // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
4780 if (g.WantCaptureMouseNextFrame != -1)
4781 {
4782 io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
4783 }
4784 else
4785 {
4786 io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
4787 io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
4788 }
4789
4790 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
4791 io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
4792 if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
4793 io.WantCaptureKeyboard = true;
4794 if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
4795 io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
4796
4797 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
4798 io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
4799}
4800
4801// Calling SetupDrawListSharedData() is followed by SetCurrentFont() which sets up the remaining data.
4802static void SetupDrawListSharedData()
4803{
4804 ImGuiContext& g = *GImGui;
4805 ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
4806 for (ImGuiViewportP* viewport : g.Viewports)
4807 virtual_space.Add(viewport->GetMainRect());
4808 g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
4809 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
4810 g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
4811 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
4812 if (g.Style.AntiAliasedLines)
4813 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
4814 if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
4815 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
4816 if (g.Style.AntiAliasedFill)
4817 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
4818 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
4819 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
4820}
4821
4822void ImGui::NewFrame()
4823{
4824 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
4825 ImGuiContext& g = *GImGui;
4826
4827 // Remove pending delete hooks before frame start.
4828 // This deferred removal avoid issues of removal while iterating the hook vector
4829 for (int n = g.Hooks.Size - 1; n >= 0; n--)
4830 if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
4831 g.Hooks.erase(&g.Hooks[n]);
4832
4833 CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
4834
4835 // Check and assert for various common IO and Configuration mistakes
4836 g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame;
4837 ErrorCheckNewFrameSanityChecks();
4838 g.ConfigFlagsCurrFrame = g.IO.ConfigFlags;
4839
4840 // Load settings on first frame, save settings when modified (after a delay)
4841 UpdateSettings();
4842
4843 g.Time += g.IO.DeltaTime;
4844 g.WithinFrameScope = true;
4845 g.FrameCount += 1;
4846 g.TooltipOverrideCount = 0;
4847 g.WindowsActiveCount = 0;
4848 g.MenusIdSubmittedThisFrame.resize(0);
4849
4850 // Calculate frame-rate for the user, as a purely luxurious feature
4851 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
4852 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
4853 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
4854 g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
4855 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
4856
4857 // Process input queue (trickle as many events as possible), turn events into writes to IO structure
4858 g.InputEventsTrail.resize(0);
4859 UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
4860
4861 // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
4862 UpdateViewportsNewFrame();
4863
4864 // Setup current font and draw list shared data
4865 // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
4866 g.IO.Fonts->Locked = true;
4867 SetupDrawListSharedData();
4868 SetCurrentFont(GetDefaultFont());
4869 IM_ASSERT(g.Font->IsLoaded());
4870
4871 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
4872 for (ImGuiViewportP* viewport : g.Viewports)
4873 {
4874 viewport->DrawData = NULL;
4875 viewport->DrawDataP.Valid = false;
4876 }
4877
4878 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
4879 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
4880 KeepAliveID(g.DragDropPayload.SourceId);
4881
4882 // Update HoveredId data
4883 if (!g.HoveredIdPreviousFrame)
4884 g.HoveredIdTimer = 0.0f;
4885 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
4886 g.HoveredIdNotActiveTimer = 0.0f;
4887 if (g.HoveredId)
4888 g.HoveredIdTimer += g.IO.DeltaTime;
4889 if (g.HoveredId && g.ActiveId != g.HoveredId)
4890 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
4891 g.HoveredIdPreviousFrame = g.HoveredId;
4892 g.HoveredId = 0;
4893 g.HoveredIdAllowOverlap = false;
4894 g.HoveredIdDisabled = false;
4895
4896 // Clear ActiveID if the item is not alive anymore.
4897 // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
4898 // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
4899 if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
4900 {
4901 IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
4902 ClearActiveID();
4903 }
4904
4905 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
4906 if (g.ActiveId)
4907 g.ActiveIdTimer += g.IO.DeltaTime;
4908 g.LastActiveIdTimer += g.IO.DeltaTime;
4909 g.ActiveIdPreviousFrame = g.ActiveId;
4910 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
4911 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4912 g.ActiveIdIsAlive = 0;
4913 g.ActiveIdHasBeenEditedThisFrame = false;
4914 g.ActiveIdPreviousFrameIsAlive = false;
4915 g.ActiveIdIsJustActivated = false;
4916 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
4917 g.TempInputId = 0;
4918 if (g.ActiveId == 0)
4919 {
4920 g.ActiveIdUsingNavDirMask = 0x00;
4921 g.ActiveIdUsingAllKeyboardKeys = false;
4922#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
4923 g.ActiveIdUsingNavInputMask = 0x00;
4924#endif
4925 }
4926
4927#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
4928 if (g.ActiveId == 0)
4929 g.ActiveIdUsingNavInputMask = 0;
4930 else if (g.ActiveIdUsingNavInputMask != 0)
4931 {
4932 // If your custom widget code used: { g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); }
4933 // Since IMGUI_VERSION_NUM >= 18804 it should be: { SetKeyOwner(ImGuiKey_Escape, g.ActiveId); SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId); }
4934 if (g.ActiveIdUsingNavInputMask & (1 << ImGuiNavInput_Cancel))
4935 SetKeyOwner(ImGuiKey_Escape, g.ActiveId);
4936 if (g.ActiveIdUsingNavInputMask & ~(1 << ImGuiNavInput_Cancel))
4937 IM_ASSERT(0); // Other values unsupported
4938 }
4939#endif
4940
4941 // Record when we have been stationary as this state is preserved while over same item.
4942 // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
4943 // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
4944 if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
4945 g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
4946 else if (g.HoverItemDelayId == 0)
4947 g.HoverItemUnlockedStationaryId = 0;
4948 if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
4949 g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
4950 else if (g.HoveredWindow == NULL)
4951 g.HoverWindowUnlockedStationaryId = 0;
4952
4953 // Update hover delay for IsItemHovered() with delays and tooltips
4954 g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
4955 if (g.HoverItemDelayId != 0)
4956 {
4957 g.HoverItemDelayTimer += g.IO.DeltaTime;
4958 g.HoverItemDelayClearTimer = 0.0f;
4959 g.HoverItemDelayId = 0;
4960 }
4961 else if (g.HoverItemDelayTimer > 0.0f)
4962 {
4963 // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
4964 // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
4965 g.HoverItemDelayClearTimer += g.IO.DeltaTime;
4966 if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
4967 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.
4968 }
4969
4970 // Drag and drop
4971 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
4972 g.DragDropAcceptIdCurr = 0;
4973 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
4974 g.DragDropWithinSource = false;
4975 g.DragDropWithinTarget = false;
4976 g.DragDropHoldJustPressedId = 0;
4977
4978 // Close popups on focus lost (currently wip/opt-in)
4979 //if (g.IO.AppFocusLost)
4980 // ClosePopupsExceptModals();
4981
4982 // Update keyboard input state
4983 UpdateKeyboardInputs();
4984
4985 //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
4986 //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
4987 //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
4988 //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
4989
4990 // Update gamepad/keyboard navigation
4991 NavUpdate();
4992
4993 // Update mouse input state
4994 UpdateMouseInputs();
4995
4996 // Undocking
4997 // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame)
4998 DockContextNewFrameUpdateUndocking(&g);
4999
5000 // Find hovered window
5001 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
5002 UpdateHoveredWindowAndCaptureFlags();
5003
5004 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
5005 UpdateMouseMovingWindowNewFrame();
5006
5007 // Background darkening/whitening
5008 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
5009 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
5010 else
5011 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
5012
5013 g.MouseCursor = ImGuiMouseCursor_Arrow;
5014 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
5015
5016 // Platform IME data: reset for the frame
5017 g.PlatformImeDataPrev = g.PlatformImeData;
5018 g.PlatformImeData.WantVisible = false;
5019
5020 // Mouse wheel scrolling, scale
5021 UpdateMouseWheel();
5022
5023 // Mark all windows as not visible and compact unused memory.
5024 IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
5025 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
5026 for (ImGuiWindow* window : g.Windows)
5027 {
5028 window->WasActive = window->Active;
5029 window->Active = false;
5030 window->WriteAccessed = false;
5031 window->BeginCountPreviousFrame = window->BeginCount;
5032 window->BeginCount = 0;
5033
5034 // Garbage collect transient buffers of recently unused windows
5035 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
5036 GcCompactTransientWindowBuffers(window);
5037 }
5038
5039 // Garbage collect transient buffers of recently unused tables
5040 for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
5041 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
5042 TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
5043 for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
5044 if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
5045 TableGcCompactTransientBuffers(&table_temp_data);
5046 if (g.GcCompactAll)
5047 GcCompactTransientMiscBuffers();
5048 g.GcCompactAll = false;
5049
5050 // Closing the focused window restore focus to the first active root window in descending z-order
5051 if (g.NavWindow && !g.NavWindow->WasActive)
5052 FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
5053
5054 // No window should be open at the beginning of the frame.
5055 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
5056 g.CurrentWindowStack.resize(0);
5057 g.BeginPopupStack.resize(0);
5058 g.ItemFlagsStack.resize(0);
5059 g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
5060 g.GroupStack.resize(0);
5061
5062 // Docking
5063 DockContextNewFrameUpdateDocking(&g);
5064
5065 // [DEBUG] Update debug features
5066#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5067 UpdateDebugToolItemPicker();
5068 UpdateDebugToolStackQueries();
5069 UpdateDebugToolFlashStyleColor();
5070 if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
5071 {
5072 g.DebugLocateId = 0;
5073 g.DebugBreakInLocateId = false;
5074 }
5075 if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
5076 {
5077 DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
5078 g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
5079 g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
5080 }
5081#endif
5082
5083 // Create implicit/fallback window - which we will only render it if the user has added something to it.
5084 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
5085 // This fallback is particularly important as it prevents ImGui:: calls from crashing.
5086 g.WithinFrameScopeWithImplicitWindow = true;
5087 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
5088 Begin("Debug##Default");
5089 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
5090
5091 // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
5092 // allowing to validate correct Begin/End behavior in user code.
5093#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5094 if (g.IO.ConfigDebugBeginReturnValueLoop)
5095 g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
5096 else
5097 g.DebugBeginReturnValueCullDepth = -1;
5098#endif
5099
5100 CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
5101}
5102
5103// FIXME: Add a more explicit sort order in the window structure.
5104static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
5105{
5106 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
5107 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
5108 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
5109 return d;
5110 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
5111 return d;
5112 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
5113}
5114
5115static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
5116{
5117 out_sorted_windows->push_back(window);
5118 if (window->Active)
5119 {
5120 int count = window->DC.ChildWindows.Size;
5121 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
5122 for (int i = 0; i < count; i++)
5123 {
5124 ImGuiWindow* child = window->DC.ChildWindows[i];
5125 if (child->Active)
5126 AddWindowToSortBuffer(out_sorted_windows, child);
5127 }
5128 }
5129}
5130
5131static void AddWindowToDrawData(ImGuiWindow* window, int layer)
5132{
5133 ImGuiContext& g = *GImGui;
5134 ImGuiViewportP* viewport = window->Viewport;
5135 IM_ASSERT(viewport != NULL);
5136 g.IO.MetricsRenderWindows++;
5137 if (window->DrawList->_Splitter._Count > 1)
5138 window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
5139 ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
5140 for (ImGuiWindow* child : window->DC.ChildWindows)
5141 if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
5142 AddWindowToDrawData(child, layer);
5143}
5144
5145static inline int GetWindowDisplayLayer(ImGuiWindow* window)
5146{
5147 return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
5148}
5149
5150// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
5151static inline void AddRootWindowToDrawData(ImGuiWindow* window)
5152{
5153 AddWindowToDrawData(window, GetWindowDisplayLayer(window));
5154}
5155
5156static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
5157{
5158 int n = builder->Layers[0]->Size;
5159 int full_size = n;
5160 for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
5161 full_size += builder->Layers[i]->Size;
5162 builder->Layers[0]->resize(full_size);
5163 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
5164 {
5165 ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
5166 if (layer->empty())
5167 continue;
5168 memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
5169 n += layer->Size;
5170 layer->resize(0);
5171 }
5172}
5173
5174static void InitViewportDrawData(ImGuiViewportP* viewport)
5175{
5176 ImGuiIO& io = ImGui::GetIO();
5177 ImDrawData* draw_data = &viewport->DrawDataP;
5178
5179 viewport->DrawData = draw_data; // Make publicly accessible
5180 viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
5181 viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
5182 viewport->DrawDataBuilder.Layers[0]->resize(0);
5183 viewport->DrawDataBuilder.Layers[1]->resize(0);
5184
5185 // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode,
5186 // and to allow applications/backends to easily skip rendering.
5187 // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure.
5188 // This is because the work has been done already, and its wasted! We should fix that and add optimizations for
5189 // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline.
5190 const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0;
5191
5192 draw_data->Valid = true;
5193 draw_data->CmdListsCount = 0;
5194 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
5195 draw_data->DisplayPos = viewport->Pos;
5196 draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size;
5197 draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis?
5198 draw_data->OwnerViewport = viewport;
5199}
5200
5201// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
5202// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
5203// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
5204// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
5205// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
5206// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
5207void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
5208{
5209 ImGuiWindow* window = GetCurrentWindow();
5210 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
5211 window->ClipRect = window->DrawList->_ClipRectStack.back();
5212}
5213
5214void ImGui::PopClipRect()
5215{
5216 ImGuiWindow* window = GetCurrentWindow();
5217 window->DrawList->PopClipRect();
5218 window->ClipRect = window->DrawList->_ClipRectStack.back();
5219}
5220
5221static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window)
5222{
5223 for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
5224 if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
5225 return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
5226 return window;
5227}
5228
5229static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
5230{
5231 if ((col & IM_COL32_A_MASK) == 0)
5232 return;
5233
5234 ImGuiViewportP* viewport = window->Viewport;
5235 ImRect viewport_rect = viewport->GetMainRect();
5236
5237 // Draw behind window by moving the draw command at the FRONT of the draw list
5238 {
5239 // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
5240 // 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.
5241 ImDrawList* draw_list = window->RootWindowDockTree->DrawList;
5242 draw_list->ChannelsMerge();
5243 if (draw_list->CmdBuffer.Size == 0)
5244 draw_list->AddDrawCmd();
5245 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)
5246 draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
5247 ImDrawCmd cmd = draw_list->CmdBuffer.back();
5248 IM_ASSERT(cmd.ElemCount == 6);
5249 draw_list->CmdBuffer.pop_back();
5250 draw_list->CmdBuffer.push_front(cmd);
5251 draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
5252 draw_list->PopClipRect();
5253 }
5254
5255 // Draw over sibling docking nodes in a same docking tree
5256 if (window->RootWindow->DockIsActive)
5257 {
5258 ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList;
5259 draw_list->ChannelsMerge();
5260 if (draw_list->CmdBuffer.Size == 0)
5261 draw_list->AddDrawCmd();
5262 draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false);
5263 RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding);
5264 draw_list->PopClipRect();
5265 }
5266}
5267
5268ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
5269{
5270 ImGuiContext& g = *GImGui;
5271 ImGuiWindow* bottom_most_visible_window = parent_window;
5272 for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
5273 {
5274 ImGuiWindow* window = g.Windows[i];
5275 if (window->Flags & ImGuiWindowFlags_ChildWindow)
5276 continue;
5277 if (!IsWindowWithinBeginStackOf(window, parent_window))
5278 break;
5279 if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
5280 bottom_most_visible_window = window;
5281 }
5282 return bottom_most_visible_window;
5283}
5284
5285// Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage.
5286// We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds().
5287static void ImGui::RenderDimmedBackgrounds()
5288{
5289 ImGuiContext& g = *GImGui;
5290 ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
5291 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
5292 return;
5293 const bool dim_bg_for_modal = (modal_window != NULL);
5294 const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
5295 if (!dim_bg_for_modal && !dim_bg_for_window_list)
5296 return;
5297
5298 ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL };
5299 if (dim_bg_for_modal)
5300 {
5301 // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
5302 ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
5303 RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
5304 viewports_already_dimmed[0] = modal_window->Viewport;
5305 }
5306 else if (dim_bg_for_window_list)
5307 {
5308 // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
5309 RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5310 if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport)
5311 RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5312 viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport;
5313 viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL;
5314
5315 // Draw border around CTRL+Tab target window
5316 ImGuiWindow* window = g.NavWindowingTargetAnim;
5317 ImGuiViewport* viewport = window->Viewport;
5318 float distance = g.FontSize;
5319 ImRect bb = window->Rect();
5320 bb.Expand(distance);
5321 if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
5322 bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
5323 window->DrawList->ChannelsMerge();
5324 if (window->DrawList->CmdBuffer.Size == 0)
5325 window->DrawList->AddDrawCmd();
5326 window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
5327 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f);
5328 window->DrawList->PopClipRect();
5329 }
5330
5331 // Draw dimming background on _other_ viewports than the ones our windows are in
5332 for (ImGuiViewportP* viewport : g.Viewports)
5333 {
5334 if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1])
5335 continue;
5336 if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window))
5337 continue;
5338 ImDrawList* draw_list = GetForegroundDrawList(viewport);
5339 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5340 draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col);
5341 }
5342}
5343
5344// 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.
5345void ImGui::EndFrame()
5346{
5347 ImGuiContext& g = *GImGui;
5348 IM_ASSERT(g.Initialized);
5349
5350 // Don't process EndFrame() multiple times.
5351 if (g.FrameCountEnded == g.FrameCount)
5352 return;
5353 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
5354
5355 CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
5356
5357 ErrorCheckEndFrameSanityChecks();
5358
5359 // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
5360 ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
5361 if (g.IO.SetPlatformImeDataFn && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
5362 {
5363 ImGuiViewport* viewport = FindViewportByID(g.PlatformImeViewport);
5364 IMGUI_DEBUG_LOG_IO("[io] Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
5365 if (viewport == NULL)
5366 viewport = GetMainViewport();
5367 g.IO.SetPlatformImeDataFn(viewport, ime_data);
5368 }
5369
5370 // Hide implicit/fallback "Debug" window if it hasn't been used
5371 g.WithinFrameScopeWithImplicitWindow = false;
5372 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
5373 g.CurrentWindow->Active = false;
5374 End();
5375
5376 // Update navigation: CTRL+Tab, wrap-around requests
5377 NavEndFrame();
5378
5379 // Update docking
5380 DockContextEndFrame(&g);
5381
5382 SetCurrentViewport(NULL, NULL);
5383
5384 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
5385 if (g.DragDropActive)
5386 {
5387 bool is_delivered = g.DragDropPayload.Delivery;
5388 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
5389 if (is_delivered || is_elapsed)
5390 ClearDragDrop();
5391 }
5392
5393 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
5394 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
5395 {
5396 g.DragDropWithinSource = true;
5397 SetTooltip("...");
5398 g.DragDropWithinSource = false;
5399 }
5400
5401 // End frame
5402 g.WithinFrameScope = false;
5403 g.FrameCountEnded = g.FrameCount;
5404
5405 // Initiate moving window + handle left-click and right-click focus
5406 UpdateMouseMovingWindowEndFrame();
5407
5408 // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
5409 UpdateViewportsEndFrame();
5410
5411 // Sort the window list so that all child windows are after their parent
5412 // We cannot do that on FocusWindow() because children may not exist yet
5413 g.WindowsTempSortBuffer.resize(0);
5414 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
5415 for (ImGuiWindow* window : g.Windows)
5416 {
5417 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
5418 continue;
5419 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
5420 }
5421
5422 // 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.
5423 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
5424 g.Windows.swap(g.WindowsTempSortBuffer);
5425 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
5426
5427 // Unlock font atlas
5428 g.IO.Fonts->Locked = false;
5429
5430 // Clear Input data for next frame
5431 g.IO.MousePosPrev = g.IO.MousePos;
5432 g.IO.AppFocusLost = false;
5433 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
5434 g.IO.InputQueueCharacters.resize(0);
5435
5436 CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
5437}
5438
5439// Prepare the data for rendering so you can call GetDrawData()
5440// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
5441// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
5442void ImGui::Render()
5443{
5444 ImGuiContext& g = *GImGui;
5445 IM_ASSERT(g.Initialized);
5446
5447 if (g.FrameCountEnded != g.FrameCount)
5448 EndFrame();
5449 if (g.FrameCountRendered == g.FrameCount)
5450 return;
5451 g.FrameCountRendered = g.FrameCount;
5452
5453 g.IO.MetricsRenderWindows = 0;
5454 CallContextHooks(&g, ImGuiContextHookType_RenderPre);
5455
5456 // Add background ImDrawList (for each active viewport)
5457 for (ImGuiViewportP* viewport : g.Viewports)
5458 {
5459 InitViewportDrawData(viewport);
5460 if (viewport->BgFgDrawLists[0] != NULL)
5461 AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
5462 }
5463
5464 // Draw modal/window whitening backgrounds
5465 RenderDimmedBackgrounds();
5466
5467 // Add ImDrawList to render
5468 ImGuiWindow* windows_to_render_top_most[2];
5469 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL;
5470 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
5471 for (ImGuiWindow* window : g.Windows)
5472 {
5473 IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
5474 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
5475 AddRootWindowToDrawData(window);
5476 }
5477 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
5478 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
5479 AddRootWindowToDrawData(windows_to_render_top_most[n]);
5480
5481 // Draw software mouse cursor if requested by io.MouseDrawCursor flag
5482 if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
5483 RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
5484
5485 // Setup ImDrawData structures for end-user
5486 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
5487 for (ImGuiViewportP* viewport : g.Viewports)
5488 {
5489 FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
5490
5491 // Add foreground ImDrawList (for each active viewport)
5492 if (viewport->BgFgDrawLists[1] != NULL)
5493 AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
5494
5495 // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
5496 ImDrawData* draw_data = &viewport->DrawDataP;
5497 IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
5498 for (ImDrawList* draw_list : draw_data->CmdLists)
5499 draw_list->_PopUnusedDrawCmd();
5500
5501 g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
5502 g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
5503 }
5504
5505 CallContextHooks(&g, ImGuiContextHookType_RenderPost);
5506}
5507
5508// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
5509// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
5510ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
5511{
5512 ImGuiContext& g = *GImGui;
5513
5514 const char* text_display_end;
5515 if (hide_text_after_double_hash)
5516 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
5517 else
5518 text_display_end = text_end;
5519
5520 ImFont* font = g.Font;
5521 const float font_size = g.FontSize;
5522 if (text == text_display_end)
5523 return ImVec2(0.0f, font_size);
5524 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
5525
5526 // Round
5527 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
5528 // FIXME: Investigate using ceilf or e.g.
5529 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
5530 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
5531 text_size.x = IM_TRUNC(text_size.x + 0.99999f);
5532
5533 return text_size;
5534}
5535
5536// Find window given position, search front-to-back
5537// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
5538// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
5539// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
5540// called, aka before the next Begin(). Moving window isn't affected.
5541// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
5542void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
5543{
5544 ImGuiContext& g = *GImGui;
5545 ImGuiWindow* hovered_window = NULL;
5546 ImGuiWindow* hovered_window_under_moving_window = NULL;
5547
5548 // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame)
5549 ImGuiViewportP* backup_moving_window_viewport = NULL;
5550 if (find_first_and_in_any_viewport == false && g.MovingWindow)
5551 {
5552 backup_moving_window_viewport = g.MovingWindow->Viewport;
5553 g.MovingWindow->Viewport = g.MouseViewport;
5554 if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
5555 hovered_window = g.MovingWindow;
5556 }
5557
5558 ImVec2 padding_regular = g.Style.TouchExtraPadding;
5559 ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
5560 for (int i = g.Windows.Size - 1; i >= 0; i--)
5561 {
5562 ImGuiWindow* window = g.Windows[i];
5563 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
5564 if (!window->Active || window->Hidden)
5565 continue;
5566 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
5567 continue;
5568 IM_ASSERT(window->Viewport);
5569 if (window->Viewport != g.MouseViewport)
5570 continue;
5571
5572 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
5573 ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
5574 if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
5575 continue;
5576
5577 // Support for one rectangular hole in any given window
5578 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
5579 if (window->HitTestHoleSize.x != 0)
5580 {
5581 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
5582 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
5583 if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
5584 continue;
5585 }
5586
5587 if (find_first_and_in_any_viewport)
5588 {
5589 hovered_window = window;
5590 break;
5591 }
5592 else
5593 {
5594 if (hovered_window == NULL)
5595 hovered_window = window;
5596 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
5597 if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree))
5598 hovered_window_under_moving_window = window;
5599 if (hovered_window && hovered_window_under_moving_window)
5600 break;
5601 }
5602 }
5603
5604 *out_hovered_window = hovered_window;
5605 if (out_hovered_window_under_moving_window != NULL)
5606 *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
5607 if (find_first_and_in_any_viewport == false && g.MovingWindow)
5608 g.MovingWindow->Viewport = backup_moving_window_viewport;
5609}
5610
5611bool ImGui::IsItemActive()
5612{
5613 ImGuiContext& g = *GImGui;
5614 if (g.ActiveId)
5615 return g.ActiveId == g.LastItemData.ID;
5616 return false;
5617}
5618
5619bool ImGui::IsItemActivated()
5620{
5621 ImGuiContext& g = *GImGui;
5622 if (g.ActiveId)
5623 if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
5624 return true;
5625 return false;
5626}
5627
5628bool ImGui::IsItemDeactivated()
5629{
5630 ImGuiContext& g = *GImGui;
5631 if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
5632 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
5633 return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
5634}
5635
5636bool ImGui::IsItemDeactivatedAfterEdit()
5637{
5638 ImGuiContext& g = *GImGui;
5639 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
5640}
5641
5642// == GetItemID() == GetFocusID()
5643bool ImGui::IsItemFocused()
5644{
5645 ImGuiContext& g = *GImGui;
5646 if (g.NavId != g.LastItemData.ID || g.NavId == 0)
5647 return false;
5648
5649 // Special handling for the dummy item after Begin() which represent the title bar or tab.
5650 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
5651 ImGuiWindow* window = g.CurrentWindow;
5652 if (g.LastItemData.ID == window->ID && window->WriteAccessed)
5653 return false;
5654
5655 return true;
5656}
5657
5658// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
5659// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
5660bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
5661{
5662 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
5663}
5664
5665bool ImGui::IsItemToggledOpen()
5666{
5667 ImGuiContext& g = *GImGui;
5668 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
5669}
5670
5671bool ImGui::IsItemToggledSelection()
5672{
5673 ImGuiContext& g = *GImGui;
5674 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
5675}
5676
5677// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
5678// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
5679// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
5680bool ImGui::IsAnyItemHovered()
5681{
5682 ImGuiContext& g = *GImGui;
5683 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
5684}
5685
5686bool ImGui::IsAnyItemActive()
5687{
5688 ImGuiContext& g = *GImGui;
5689 return g.ActiveId != 0;
5690}
5691
5692bool ImGui::IsAnyItemFocused()
5693{
5694 ImGuiContext& g = *GImGui;
5695 return g.NavId != 0 && !g.NavDisableHighlight;
5696}
5697
5698bool ImGui::IsItemVisible()
5699{
5700 ImGuiContext& g = *GImGui;
5701 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
5702}
5703
5704bool ImGui::IsItemEdited()
5705{
5706 ImGuiContext& g = *GImGui;
5707 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
5708}
5709
5710// Allow next item to be overlapped by subsequent items.
5711// This works by requiring HoveredId to match for two subsequent frames,
5712// so if a following items overwrite it our interactions will naturally be disabled.
5713void ImGui::SetNextItemAllowOverlap()
5714{
5715 ImGuiContext& g = *GImGui;
5716 g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
5717}
5718
5719#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5720// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
5721// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
5722void ImGui::SetItemAllowOverlap()
5723{
5724 ImGuiContext& g = *GImGui;
5725 ImGuiID id = g.LastItemData.ID;
5726 if (g.HoveredId == id)
5727 g.HoveredIdAllowOverlap = true;
5728 if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
5729 g.ActiveIdAllowOverlap = true;
5730}
5731#endif
5732
5733// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function.
5734void ImGui::SetActiveIdUsingAllKeyboardKeys()
5735{
5736 ImGuiContext& g = *GImGui;
5737 IM_ASSERT(g.ActiveId != 0);
5738 g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
5739 g.ActiveIdUsingAllKeyboardKeys = true;
5740 NavMoveRequestCancel();
5741}
5742
5743ImGuiID ImGui::GetItemID()
5744{
5745 ImGuiContext& g = *GImGui;
5746 return g.LastItemData.ID;
5747}
5748
5749ImVec2 ImGui::GetItemRectMin()
5750{
5751 ImGuiContext& g = *GImGui;
5752 return g.LastItemData.Rect.Min;
5753}
5754
5755ImVec2 ImGui::GetItemRectMax()
5756{
5757 ImGuiContext& g = *GImGui;
5758 return g.LastItemData.Rect.Max;
5759}
5760
5761ImVec2 ImGui::GetItemRectSize()
5762{
5763 ImGuiContext& g = *GImGui;
5764 return g.LastItemData.Rect.GetSize();
5765}
5766
5767// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
5768// ImGuiChildFlags_Border is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
5769bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
5770{
5771 ImGuiID id = GetCurrentWindow()->GetID(str_id);
5772 return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
5773}
5774
5775bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
5776{
5777 return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
5778}
5779
5780bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
5781{
5782 ImGuiContext& g = *GImGui;
5783 ImGuiWindow* parent_window = g.CurrentWindow;
5784 IM_ASSERT(id != 0);
5785
5786 // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
5787 const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle;
5788 IM_UNUSED(ImGuiChildFlags_SupportedMask_);
5789 IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
5790 IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
5791 if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
5792 {
5793 IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
5794 IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
5795 }
5796#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5797 if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
5798 child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
5799#endif
5800 if (child_flags & ImGuiChildFlags_AutoResizeX)
5801 child_flags &= ~ImGuiChildFlags_ResizeX;
5802 if (child_flags & ImGuiChildFlags_AutoResizeY)
5803 child_flags &= ~ImGuiChildFlags_ResizeY;
5804
5805 // Set window flags
5806 window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking;
5807 window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
5808 if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
5809 window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
5810 if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
5811 window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
5812
5813 // Special framed style
5814 if (child_flags & ImGuiChildFlags_FrameStyle)
5815 {
5816 PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
5817 PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
5818 PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
5819 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
5820 child_flags |= ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding;
5821 window_flags |= ImGuiWindowFlags_NoMove;
5822 }
5823
5824 // Forward child flags
5825 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;
5826 g.NextWindowData.ChildFlags = child_flags;
5827
5828 // Forward size
5829 // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
5830 // (the alternative would to store conditional flags per axis, which is possible but more code)
5831 const ImVec2 size_avail = GetContentRegionAvail();
5832 const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
5833 const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
5834 SetNextWindowSize(size);
5835
5836 // 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.
5837 // 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.
5838 // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
5839 const char* temp_window_name;
5840 /*if (name && parent_window->IDStack.back() == parent_window->ID)
5841 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
5842 else*/
5843 if (name)
5844 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
5845 else
5846 ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
5847
5848 // Set style
5849 const float backup_border_size = g.Style.ChildBorderSize;
5850 if ((child_flags & ImGuiChildFlags_Border) == 0)
5851 g.Style.ChildBorderSize = 0.0f;
5852
5853 // Begin into window
5854 const bool ret = Begin(temp_window_name, NULL, window_flags);
5855
5856 // Restore style
5857 g.Style.ChildBorderSize = backup_border_size;
5858 if (child_flags & ImGuiChildFlags_FrameStyle)
5859 {
5860 PopStyleVar(3);
5861 PopStyleColor();
5862 }
5863
5864 ImGuiWindow* child_window = g.CurrentWindow;
5865 child_window->ChildId = id;
5866
5867 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5868 // While this is not really documented/defined, it seems that the expected thing to do.
5869 if (child_window->BeginCount == 1)
5870 parent_window->DC.CursorPos = child_window->Pos;
5871
5872 // Process navigation-in immediately so NavInit can run on first frame
5873 // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
5874 const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
5875 if (g.ActiveId == temp_id_for_activation)
5876 ClearActiveID();
5877 if (g.NavActivateId == id && !(window_flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
5878 {
5879 FocusWindow(child_window);
5880 NavInitWindow(child_window, false);
5881 SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5882 g.ActiveIdSource = g.NavInputSource;
5883 }
5884 return ret;
5885}
5886
5887void ImGui::EndChild()
5888{
5889 ImGuiContext& g = *GImGui;
5890 ImGuiWindow* child_window = g.CurrentWindow;
5891
5892 IM_ASSERT(g.WithinEndChild == false);
5893 IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
5894
5895 g.WithinEndChild = true;
5896 ImVec2 child_size = child_window->Size;
5897 End();
5898 if (child_window->BeginCount == 1)
5899 {
5900 ImGuiWindow* parent_window = g.CurrentWindow;
5901 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
5902 ItemSize(child_size);
5903 if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !(child_window->Flags & ImGuiWindowFlags_NavFlattened))
5904 {
5905 ItemAdd(bb, child_window->ChildId);
5906 RenderNavHighlight(bb, child_window->ChildId);
5907
5908 // 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)
5909 if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
5910 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_Compact);
5911 }
5912 else
5913 {
5914 // Not navigable into
5915 // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
5916 // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
5917 ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
5918
5919 // But when flattened we directly reach items, adjust active layer mask accordingly
5920 if (child_window->Flags & ImGuiWindowFlags_NavFlattened)
5921 parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
5922 }
5923 if (g.HoveredWindow == child_window)
5924 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
5925 }
5926 g.WithinEndChild = false;
5927 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
5928}
5929
5930static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5931{
5932 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
5933 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
5934 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5935 window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags);
5936}
5937
5938ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5939{
5940 ImGuiContext& g = *GImGui;
5941 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5942}
5943
5944ImGuiWindow* ImGui::FindWindowByName(const char* name)
5945{
5946 ImGuiID id = ImHashStr(name);
5947 return FindWindowByID(id);
5948}
5949
5950static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5951{
5952 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5953 window->ViewportPos = main_viewport->Pos;
5954 if (settings->ViewportId)
5955 {
5956 window->ViewportId = settings->ViewportId;
5957 window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y);
5958 }
5959 window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y));
5960 if (settings->Size.x > 0 && settings->Size.y > 0)
5961 window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
5962 window->Collapsed = settings->Collapsed;
5963 window->DockId = settings->DockId;
5964 window->DockOrder = settings->DockOrder;
5965}
5966
5967static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
5968{
5969 ImGuiContext& g = *GImGui;
5970
5971 const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
5972 const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
5973 if ((just_created || child_flag_changed) && !new_is_explicit_child)
5974 {
5975 IM_ASSERT(!g.WindowsFocusOrder.contains(window));
5976 g.WindowsFocusOrder.push_back(window);
5977 window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
5978 }
5979 else if (!just_created && child_flag_changed && new_is_explicit_child)
5980 {
5981 IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
5982 for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
5983 g.WindowsFocusOrder[n]->FocusOrder--;
5984 g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
5985 window->FocusOrder = -1;
5986 }
5987 window->IsExplicitChild = new_is_explicit_child;
5988}
5989
5990static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5991{
5992 // Initial window state with e.g. default/arbitrary window position
5993 // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5994 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5995 window->Pos = main_viewport->Pos + ImVec2(60, 60);
5996 window->Size = window->SizeFull = ImVec2(0, 0);
5997 window->ViewportPos = main_viewport->Pos;
5998 window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
5999
6000 if (settings != NULL)
6001 {
6002 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
6003 ApplyWindowSettings(window, settings);
6004 }
6005 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
6006
6007 if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
6008 {
6009 window->AutoFitFramesX = window->AutoFitFramesY = 2;
6010 window->AutoFitOnlyGrows = false;
6011 }
6012 else
6013 {
6014 if (window->Size.x <= 0.0f)
6015 window->AutoFitFramesX = 2;
6016 if (window->Size.y <= 0.0f)
6017 window->AutoFitFramesY = 2;
6018 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
6019 }
6020}
6021
6022static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
6023{
6024 // Create window the first time
6025 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
6026 ImGuiContext& g = *GImGui;
6027 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
6028 window->Flags = flags;
6029 g.WindowsById.SetVoidPtr(window->ID, window);
6030
6031 ImGuiWindowSettings* settings = NULL;
6032 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
6033 if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
6034 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
6035
6036 InitOrLoadWindowSettings(window, settings);
6037
6038 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
6039 g.Windows.push_front(window); // Quite slow but rare and only once
6040 else
6041 g.Windows.push_back(window);
6042
6043 return window;
6044}
6045
6046static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window)
6047{
6048 return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window;
6049}
6050
6051static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
6052{
6053 return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
6054}
6055
6056static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
6057{
6058 // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
6059 // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
6060 // Perhaps should tend further a neater test for this.
6061 ImGuiContext& g = *GImGui;
6062 ImVec2 size_min;
6063 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
6064 {
6065 size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
6066 size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
6067 }
6068 else
6069 {
6070 size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
6071 size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
6072 }
6073
6074 // Reduce artifacts with very small windows
6075 ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
6076 size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
6077 return size_min;
6078}
6079
6080static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
6081{
6082 ImGuiContext& g = *GImGui;
6083 ImVec2 new_size = size_desired;
6084 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
6085 {
6086 // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
6087 ImRect cr = g.NextWindowData.SizeConstraintRect;
6088 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
6089 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
6090 if (g.NextWindowData.SizeCallback)
6091 {
6092 ImGuiSizeCallbackData data;
6093 data.UserData = g.NextWindowData.SizeCallbackUserData;
6094 data.Pos = window->Pos;
6095 data.CurrentSize = window->SizeFull;
6096 data.DesiredSize = new_size;
6097 g.NextWindowData.SizeCallback(&data);
6098 new_size = data.DesiredSize;
6099 }
6100 new_size.x = IM_TRUNC(new_size.x);
6101 new_size.y = IM_TRUNC(new_size.y);
6102 }
6103
6104 // Minimum size
6105 ImVec2 size_min = CalcWindowMinSize(window);
6106 return ImMax(new_size, size_min);
6107}
6108
6109static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
6110{
6111 bool preserve_old_content_sizes = false;
6112 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6113 preserve_old_content_sizes = true;
6114 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
6115 preserve_old_content_sizes = true;
6116 if (preserve_old_content_sizes)
6117 {
6118 *content_size_current = window->ContentSize;
6119 *content_size_ideal = window->ContentSizeIdeal;
6120 return;
6121 }
6122
6123 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
6124 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
6125 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);
6126 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);
6127}
6128
6129static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
6130{
6131 ImGuiContext& g = *GImGui;
6132 ImGuiStyle& style = g.Style;
6133 const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
6134 const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
6135 ImVec2 size_pad = window->WindowPadding * 2.0f;
6136 ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
6137 if (window->Flags & ImGuiWindowFlags_Tooltip)
6138 {
6139 // Tooltip always resize
6140 return size_desired;
6141 }
6142 else
6143 {
6144 // Maximum window size is determined by the viewport size or monitor size
6145 ImVec2 size_min = CalcWindowMinSize(window);
6146 ImVec2 size_max = (window->ViewportOwned || ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6147 const int monitor_idx = window->ViewportAllowPlatformMonitorExtend;
6148 if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0)
6149 size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6150 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, size_max));
6151
6152 // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
6153 // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
6154 // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
6155 if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & ImGuiChildFlags_ResizeY))
6156 size_auto_fit.y = window->SizeFull.y;
6157 else if (!(window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY))
6158 size_auto_fit.x = window->SizeFull.x;
6159
6160 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
6161 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
6162 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6163 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);
6164 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);
6165 if (will_have_scrollbar_x)
6166 size_auto_fit.y += style.ScrollbarSize;
6167 if (will_have_scrollbar_y)
6168 size_auto_fit.x += style.ScrollbarSize;
6169 return size_auto_fit;
6170 }
6171}
6172
6173ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
6174{
6175 ImVec2 size_contents_current;
6176 ImVec2 size_contents_ideal;
6177 CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
6178 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
6179 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6180 return size_final;
6181}
6182
6183static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
6184{
6185 if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
6186 return ImGuiCol_PopupBg;
6187 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive)
6188 return ImGuiCol_ChildBg;
6189 return ImGuiCol_WindowBg;
6190}
6191
6192static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
6193{
6194 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
6195 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
6196 ImVec2 size_expected = pos_max - pos_min;
6197 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
6198 *out_pos = pos_min;
6199 if (corner_norm.x == 0.0f)
6200 out_pos->x -= (size_constrained.x - size_expected.x);
6201 if (corner_norm.y == 0.0f)
6202 out_pos->y -= (size_constrained.y - size_expected.y);
6203 *out_size = size_constrained;
6204}
6205
6206// Data for resizing from resize grip / corner
6213static const ImGuiResizeGripDef resize_grip_def[4] =
6214{
6215 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
6216 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
6217 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
6218 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
6219};
6220
6221// Data for resizing from borders
6223{
6224 ImVec2 InnerDir; // Normal toward inside
6225 ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left)
6226 float OuterAngle; // Angle toward outside
6227};
6228static const ImGuiResizeBorderDef resize_border_def[4] =
6229{
6230 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
6231 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
6232 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
6233 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down
6234};
6235
6236static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
6237{
6238 ImRect rect = window->Rect();
6239 if (thickness == 0.0f)
6240 rect.Max -= ImVec2(1, 1);
6241 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); }
6242 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); }
6243 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); }
6244 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); }
6245 IM_ASSERT(0);
6246 return ImRect();
6247}
6248
6249// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
6250ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
6251{
6252 IM_ASSERT(n >= 0 && n < 4);
6253 ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
6254 id = ImHashStr("#RESIZE", 0, id);
6255 id = ImHashData(&n, sizeof(int), id);
6256 return id;
6257}
6258
6259// Borders (Left, Right, Up, Down)
6260ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
6261{
6262 IM_ASSERT(dir >= 0 && dir < 4);
6263 int n = (int)dir + 4;
6264 ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID;
6265 id = ImHashStr("#RESIZE", 0, id);
6266 id = ImHashData(&n, sizeof(int), id);
6267 return id;
6268}
6269
6270// Handle resize for: Resize Grips, Borders, Gamepad
6271// Return true when using auto-fit (double-click on resize grip)
6272static 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)
6273{
6274 ImGuiContext& g = *GImGui;
6275 ImGuiWindowFlags flags = window->Flags;
6276
6277 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6278 return false;
6279 if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
6280 return false;
6281
6282 int ret_auto_fit_mask = 0x00;
6283 const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6284 const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
6285 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
6286
6287 ImRect clamp_rect = visibility_rect;
6288 const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
6289 if (window_move_from_title_bar)
6290 clamp_rect.Min.y -= window->TitleBarHeight;
6291
6292 ImVec2 pos_target(FLT_MAX, FLT_MAX);
6293 ImVec2 size_target(FLT_MAX, FLT_MAX);
6294
6295 // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time).
6296 // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits.
6297 // This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it.
6298 // - 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.
6299 // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold).
6300 // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup.
6301 const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration);
6302 if (clip_with_viewport_rect)
6303 window->ClipRect = window->Viewport->GetMainRect();
6304
6305 // Resize grips and borders are on layer 1
6306 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6307
6308 // Manual resize grips
6309 PushID("#RESIZE");
6310 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6311 {
6312 const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
6313 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
6314
6315 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6316 bool hovered, held;
6317 ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
6318 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
6319 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
6320 ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
6321 ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
6322 ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6323 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
6324 if (hovered || held)
6325 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
6326
6327 if (held && g.IO.MouseDoubleClicked[0])
6328 {
6329 // Auto-fit when double-clicking
6330 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6331 ret_auto_fit_mask = 0x03; // Both axises
6332 ClearActiveID();
6333 }
6334 else if (held)
6335 {
6336 // Resize from any of the four corners
6337 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6338 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);
6339 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);
6340 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
6341 corner_target = ImClamp(corner_target, clamp_min, clamp_max);
6342 CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
6343 }
6344
6345 // Only lower-left grip is visible before hovering/activating
6346 if (resize_grip_n == 0 || held || hovered)
6347 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
6348 }
6349
6350 int resize_border_mask = 0x00;
6351 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6352 resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
6353 else
6354 resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
6355 for (int border_n = 0; border_n < 4; border_n++)
6356 {
6357 if ((resize_border_mask & (1 << border_n)) == 0)
6358 continue;
6359 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6360 const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
6361
6362 bool hovered, held;
6363 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
6364 ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
6365 ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
6366 ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6367 //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
6368 if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
6369 hovered = false;
6370 if (hovered || held)
6371 g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
6372 if (held && g.IO.MouseDoubleClicked[0])
6373 {
6374 // Double-clicking bottom or right border auto-fit on this axis
6375 // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
6376 // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
6377 if (border_n == 1 || border_n == 3) // Right and bottom border
6378 {
6379 size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
6380 ret_auto_fit_mask |= (1 << axis);
6381 hovered = held = false; // So border doesn't show highlighted at new position
6382 }
6383 ClearActiveID();
6384 }
6385 else if (held)
6386 {
6387 // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
6388 // 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.
6389 // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
6390 const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false, true));
6391 if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
6392 {
6393 g.WindowResizeBorderExpectedRect = border_rect;
6394 g.WindowResizeRelativeMode = false;
6395 }
6396 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
6397 g.WindowResizeRelativeMode = true;
6398
6399 const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
6400 const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
6401 const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.
6402
6403 // Use absolute mode position
6404 ImVec2 border_target = window->Pos;
6405 border_target[axis] = border_target_abs_mode_for_axis;
6406
6407 // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
6408 bool ignore_resize = false;
6409 if (g.WindowResizeRelativeMode)
6410 {
6411 //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
6412 border_target[axis] = border_target_rel_mode_for_axis;
6413 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))
6414 ignore_resize = true;
6415 }
6416
6417 // Clamp, apply
6418 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);
6419 ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
6420 border_target = ImClamp(border_target, clamp_min, clamp_max);
6421 if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
6422 {
6423 ImGuiWindowFlags parent_flags = window->ParentWindow->Flags;
6424 ImRect border_limit_rect = window->ParentWindow->InnerRect;
6425 border_limit_rect.Expand(ImVec2(-ImMax(window->WindowPadding.x, window->WindowBorderSize), -ImMax(window->WindowPadding.y, window->WindowBorderSize)));
6426 if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))
6427 border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x);
6428 if (parent_flags & ImGuiWindowFlags_NoScrollbar)
6429 border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y);
6430 }
6431 if (!ignore_resize)
6432 CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
6433 }
6434 if (hovered)
6435 *border_hovered = border_n;
6436 if (held)
6437 *border_held = border_n;
6438 }
6439 PopID();
6440
6441 // Restore nav layer
6442 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6443
6444 // Navigation resize (keyboard/gamepad)
6445 // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
6446 // Not even sure the callback works here.
6447 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window)
6448 {
6449 ImVec2 nav_resize_dir;
6450 if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
6451 nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
6452 if (g.NavInputSource == ImGuiInputSource_Gamepad)
6453 nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
6454 if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
6455 {
6456 const float NAV_RESIZE_SPEED = 600.0f;
6457 const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
6458 g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
6459 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
6460 g.NavWindowingToggleLayer = false;
6461 g.NavDisableMouseHover = true;
6462 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
6463 ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
6464 if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
6465 {
6466 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
6467 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
6468 g.NavWindowingAccumDeltaSize -= accum_floored;
6469 }
6470 }
6471 }
6472
6473 // Apply back modified position/size to window
6474 const ImVec2 curr_pos = window->Pos;
6475 const ImVec2 curr_size = window->SizeFull;
6476 if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
6477 window->Size.x = window->SizeFull.x = size_target.x;
6478 if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
6479 window->Size.y = window->SizeFull.y = size_target.y;
6480 if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
6481 window->Pos.x = ImTrunc(pos_target.x);
6482 if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
6483 window->Pos.y = ImTrunc(pos_target.y);
6484 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)
6485 MarkIniSettingsDirty(window);
6486
6487 // Recalculate next expected border expected coordinates
6488 if (*border_held != -1)
6489 g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
6490
6491 return ret_auto_fit_mask;
6492}
6493
6494static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
6495{
6496 ImGuiContext& g = *GImGui;
6497 ImVec2 size_for_clamping = window->Size;
6498 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && window->DockNodeAsHost)
6499 size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here.
6500 else if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6501 size_for_clamping.y = window->TitleBarHeight;
6502 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
6503}
6504
6505static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
6506{
6507 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6508 const float rounding = window->WindowRounding;
6509 const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
6510 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);
6511 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);
6512 window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
6513}
6514
6515static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
6516{
6517 ImGuiContext& g = *GImGui;
6518 const float border_size = window->WindowBorderSize;
6519 const ImU32 border_col = GetColorU32(ImGuiCol_Border);
6520 if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
6521 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
6522 else if (border_size > 0.0f)
6523 {
6524 if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
6525 RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
6526 if (window->ChildFlags & ImGuiChildFlags_ResizeY)
6527 RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
6528 }
6529 if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
6530 {
6531 const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
6532 const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
6533 RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
6534 }
6535 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
6536 {
6537 float y = window->Pos.y + window->TitleBarHeight - 1;
6538 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);
6539 }
6540}
6541
6542// Draw background and borders
6543// Draw and handle scrollbars
6544void 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)
6545{
6546 ImGuiContext& g = *GImGui;
6547 ImGuiStyle& style = g.Style;
6548 ImGuiWindowFlags flags = window->Flags;
6549
6550 // Ensure that ScrollBar doesn't read last frame's SkipItems
6551 IM_ASSERT(window->BeginCount == 0);
6552 window->SkipItems = false;
6553
6554 // Draw window + handle manual resize
6555 // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
6556 const float window_rounding = window->WindowRounding;
6557 const float window_border_size = window->WindowBorderSize;
6558 if (window->Collapsed)
6559 {
6560 // Title bar only
6561 const float backup_border_size = style.FrameBorderSize;
6562 g.Style.FrameBorderSize = window->WindowBorderSize;
6563 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
6564 if (window->ViewportOwned)
6565 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)
6566 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
6567 g.Style.FrameBorderSize = backup_border_size;
6568 }
6569 else
6570 {
6571 // Window background
6572 if (!(flags & ImGuiWindowFlags_NoBackground))
6573 {
6574 bool is_docking_transparent_payload = false;
6575 if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload)
6576 if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window)
6577 is_docking_transparent_payload = true;
6578
6579 ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
6580 if (window->ViewportOwned)
6581 {
6582 bg_col |= IM_COL32_A_MASK; // No alpha
6583 if (is_docking_transparent_payload)
6584 window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA;
6585 }
6586 else
6587 {
6588 // Adjust alpha. For docking
6589 bool override_alpha = false;
6590 float alpha = 1.0f;
6591 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
6592 {
6593 alpha = g.NextWindowData.BgAlphaVal;
6594 override_alpha = true;
6595 }
6596 if (is_docking_transparent_payload)
6597 {
6598 alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override?
6599 override_alpha = true;
6600 }
6601 if (override_alpha)
6602 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
6603 }
6604
6605 // Render, for docked windows and host windows we ensure bg goes before decorations
6606 if (window->DockIsActive)
6607 window->DockNode->LastBgColor = bg_col;
6608 ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList;
6609 if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
6610 bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
6611 bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
6612 if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost))
6613 bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
6614 }
6615 if (window->DockIsActive)
6616 window->DockNode->IsBgDrawnThisFrame = true;
6617
6618 // Title bar
6619 // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
6620 // in order for their pos/size to be matching their undocking state.)
6621 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
6622 {
6623 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
6624 if (window->ViewportOwned)
6625 title_bar_col |= IM_COL32_A_MASK; // No alpha
6626 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
6627 }
6628
6629 // Menu bar
6630 if (flags & ImGuiWindowFlags_MenuBar)
6631 {
6632 ImRect menu_bar_rect = window->MenuBarRect();
6633 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.
6634 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);
6635 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
6636 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
6637 }
6638
6639 // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock
6640 ImGuiDockNode* node = window->DockNode;
6641 if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar())
6642 {
6643 float unhide_sz_draw = ImTrunc(g.FontSize * 0.70f);
6644 float unhide_sz_hit = ImTrunc(g.FontSize * 0.55f);
6645 ImVec2 p = node->Pos;
6646 ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit));
6647 ImGuiID unhide_id = window->GetID("#UNHIDE");
6648 KeepAliveID(unhide_id);
6649 bool hovered, held;
6650 if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren))
6651 node->WantHiddenTabBarToggle = true;
6652 else if (held && IsMouseDragging(0))
6653 StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button
6654
6655 // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size..
6656 ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
6657 window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col);
6658 }
6659
6660 // Scrollbars
6661 if (window->ScrollbarX)
6662 Scrollbar(ImGuiAxis_X);
6663 if (window->ScrollbarY)
6664 Scrollbar(ImGuiAxis_Y);
6665
6666 // Render resize grips (after their input handling so we don't have a frame of latency)
6667 if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
6668 {
6669 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6670 {
6671 const ImU32 col = resize_grip_col[resize_grip_n];
6672 if ((col & IM_COL32_A_MASK) == 0)
6673 continue;
6674 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
6675 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
6676 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)));
6677 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)));
6678 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);
6679 window->DrawList->PathFillConvex(col);
6680 }
6681 }
6682
6683 // Borders (for dock node host they will be rendered over after the tab bar)
6684 if (handle_borders_and_resize_grips && !window->DockNodeAsHost)
6685 RenderWindowOuterBorders(window);
6686 }
6687}
6688
6689// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead.
6690// Render title text, collapse button, close button
6691void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
6692{
6693 ImGuiContext& g = *GImGui;
6694 ImGuiStyle& style = g.Style;
6695 ImGuiWindowFlags flags = window->Flags;
6696
6697 const bool has_close_button = (p_open != NULL);
6698 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
6699
6700 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
6701 // 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?
6702 const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
6703 g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
6704 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6705
6706 // Layout buttons
6707 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
6708 float pad_l = style.FramePadding.x;
6709 float pad_r = style.FramePadding.x;
6710 float button_sz = g.FontSize;
6711 ImVec2 close_button_pos;
6712 ImVec2 collapse_button_pos;
6713 if (has_close_button)
6714 {
6715 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
6716 pad_r += button_sz + style.ItemInnerSpacing.x;
6717 }
6718 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
6719 {
6720 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
6721 pad_r += button_sz + style.ItemInnerSpacing.x;
6722 }
6723 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
6724 {
6725 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
6726 pad_l += button_sz + style.ItemInnerSpacing.x;
6727 }
6728
6729 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
6730 if (has_collapse_button)
6731 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL))
6732 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
6733
6734 // Close button
6735 if (has_close_button)
6736 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
6737 *p_open = false;
6738
6739 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6740 g.CurrentItemFlags = item_flags_backup;
6741
6742 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
6743 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
6744 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
6745 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
6746
6747 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
6748 // while uncentered title text will still reach edges correctly.
6749 if (pad_l > style.FramePadding.x)
6750 pad_l += g.Style.ItemInnerSpacing.x;
6751 if (pad_r > style.FramePadding.x)
6752 pad_r += g.Style.ItemInnerSpacing.x;
6753 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
6754 {
6755 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
6756 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
6757 pad_l = ImMax(pad_l, pad_extend * centerness);
6758 pad_r = ImMax(pad_r, pad_extend * centerness);
6759 }
6760
6761 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);
6762 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);
6763 if (flags & ImGuiWindowFlags_UnsavedDocument)
6764 {
6765 ImVec2 marker_pos;
6766 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);
6767 marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
6768 if (marker_pos.x > layout_r.Min.x)
6769 {
6770 RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
6771 clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
6772 }
6773 }
6774 //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
6775 //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
6776 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
6777}
6778
6779void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
6780{
6781 window->ParentWindow = parent_window;
6782 window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
6783 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
6784 {
6785 window->RootWindowDockTree = parent_window->RootWindowDockTree;
6786 if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
6787 window->RootWindow = parent_window->RootWindow;
6788 }
6789 if (parent_window && (flags & ImGuiWindowFlags_Popup))
6790 window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
6791 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ?
6792 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
6793 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
6794 {
6795 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
6796 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
6797 }
6798}
6799
6800// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
6801// This is designed as a toy/test-bed for
6802void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
6803{
6804 ImGuiContext& g = *GImGui;
6805 window->SkipRefresh = false;
6806 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
6807 return;
6808 if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
6809 {
6810 // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
6811 if (window->Appearing) // If currently appearing
6812 return;
6813 if (window->Hidden) // If was hidden (previous frame)
6814 return;
6815 if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow && window->RootWindow == g.HoveredWindow->RootWindow)
6816 return;
6817 if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow && window->RootWindow == g.NavWindow->RootWindow)
6818 return;
6819 window->DrawList = NULL;
6820 window->SkipRefresh = true;
6821 }
6822}
6823
6824// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
6825// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
6826// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
6827// - WindowA // FindBlockingModal() returns Modal1
6828// - WindowB // .. returns Modal1
6829// - Modal1 // .. returns Modal2
6830// - WindowC // .. returns Modal2
6831// - WindowD // .. returns Modal2
6832// - Modal2 // .. returns Modal2
6833// - WindowE // .. returns NULL
6834// Notes:
6835// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
6836// Only difference is here we check for ->Active/WasActive but it may be unnecessary.
6837ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
6838{
6839 ImGuiContext& g = *GImGui;
6840 if (g.OpenPopupStack.Size <= 0)
6841 return NULL;
6842
6843 // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
6844 for (ImGuiPopupData& popup_data : g.OpenPopupStack)
6845 {
6846 ImGuiWindow* popup_window = popup_data.Window;
6847 if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
6848 continue;
6849 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.
6850 continue;
6851 if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
6852 return popup_window;
6853 if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal
6854 continue;
6855 return popup_window; // Place window right below first block modal
6856 }
6857 return NULL;
6858}
6859
6860// Push a new Dear ImGui window to add widgets to.
6861// - 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.
6862// - Begin/End can be called multiple times during the frame with the same window name to append content.
6863// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
6864// 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.
6865// - 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.
6866// - 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.
6867bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
6868{
6869 ImGuiContext& g = *GImGui;
6870 const ImGuiStyle& style = g.Style;
6871 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
6872 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
6873 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
6874
6875 // Find or create
6876 ImGuiWindow* window = FindWindowByName(name);
6877 const bool window_just_created = (window == NULL);
6878 if (window_just_created)
6879 window = CreateNewWindow(name, flags);
6880
6881 // [DEBUG] Debug break requested by user
6882 if (g.DebugBreakInWindow == window->ID)
6883 IM_DEBUG_BREAK();
6884
6885 // Automatically disable manual moving/resizing when NoInputs is set
6886 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
6887 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
6888
6889 if (flags & ImGuiWindowFlags_NavFlattened)
6890 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
6891
6892 const int current_frame = g.FrameCount;
6893 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
6894 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
6895
6896 // Update the Appearing flag (note: the BeginDocked() path may also set this to true later)
6897 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
6898 if (flags & ImGuiWindowFlags_Popup)
6899 {
6900 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
6901 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
6902 window_just_activated_by_user |= (window != popup_ref.Window);
6903 }
6904
6905 // Update Flags, LastFrameActive, BeginOrderXXX fields
6906 const bool window_was_appearing = window->Appearing;
6907 if (first_begin_of_the_frame)
6908 {
6909 UpdateWindowInFocusOrderList(window, window_just_created, flags);
6910 window->Appearing = window_just_activated_by_user;
6911 if (window->Appearing)
6912 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
6913 window->FlagsPreviousFrame = window->Flags;
6914 window->Flags = (ImGuiWindowFlags)flags;
6915 window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
6916 window->LastFrameActive = current_frame;
6917 window->LastTimeActive = (float)g.Time;
6918 window->BeginOrderWithinParent = 0;
6919 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
6920 }
6921 else
6922 {
6923 flags = window->Flags;
6924 }
6925
6926 // Docking
6927 // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
6928 IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
6929 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock)
6930 SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
6931 if (first_begin_of_the_frame)
6932 {
6933 bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL);
6934 bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window);
6935 bool dock_node_was_visible = window->DockNodeIsVisible;
6936 bool dock_tab_was_visible = window->DockTabIsVisible;
6937 if (has_dock_node || new_auto_dock_node)
6938 {
6939 BeginDocked(window, p_open);
6940 flags = window->Flags;
6941 if (window->DockIsActive)
6942 {
6943 IM_ASSERT(window->DockNode != NULL);
6944 g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints
6945 }
6946
6947 // Amend the Appearing flag
6948 if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing)
6949 {
6950 window->Appearing = true;
6951 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
6952 }
6953 }
6954 else
6955 {
6956 window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
6957 }
6958 }
6959
6960 // 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
6961 ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
6962 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
6963 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
6964
6965 // We allow window memory to be compacted so recreate the base stack when needed.
6966 if (window->IDStack.Size == 0)
6967 window->IDStack.push_back(window->ID);
6968
6969 // Add to stack
6970 g.CurrentWindow = window;
6971 ImGuiWindowStackData window_stack_data;
6972 window_stack_data.Window = window;
6973 window_stack_data.ParentLastItemDataBackup = g.LastItemData;
6974 window_stack_data.StackSizesOnBegin.SetToContextState(&g);
6975 g.CurrentWindowStack.push_back(window_stack_data);
6976 if (flags & ImGuiWindowFlags_ChildMenu)
6977 g.BeginMenuDepth++;
6978
6979 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
6980 if (first_begin_of_the_frame)
6981 {
6982 UpdateWindowParentAndRootLinks(window, flags, parent_window);
6983 window->ParentWindowInBeginStack = parent_window_in_stack;
6984
6985 // Focus route
6986 // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
6987 // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
6988 window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL;
6989 if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL)
6990 if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute)
6991 window->ParentWindowForFocusRoute = window->DockNode->HostWindow;
6992
6993 // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute()
6994 if (window->WindowClass.FocusRouteParentWindowId != 0)
6995 {
6996 window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId);
6997 IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId.
6998 }
6999 }
7000
7001 // Add to focus scope stack
7002 PushFocusScope((flags & ImGuiWindowFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
7003 window->NavRootFocusScopeId = g.CurrentFocusScopeId;
7004
7005 // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
7006 if (flags & ImGuiWindowFlags_Popup)
7007 {
7008 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7009 popup_ref.Window = window;
7010 popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
7011 g.BeginPopupStack.push_back(popup_ref);
7012 window->PopupId = popup_ref.PopupId;
7013 }
7014
7015 // Process SetNextWindow***() calls
7016 // (FIXME: Consider splitting the HasXXX flags into X/Y components
7017 bool window_pos_set_by_api = false;
7018 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
7019 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
7020 {
7021 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
7022 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
7023 {
7024 // May be processed on the next frame if this is our first frame and we are measuring size
7025 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
7026 window->SetWindowPosVal = g.NextWindowData.PosVal;
7027 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
7028 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7029 }
7030 else
7031 {
7032 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
7033 }
7034 }
7035 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
7036 {
7037 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
7038 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
7039 if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
7040 g.NextWindowData.SizeVal.x = window->SizeFull.x;
7041 if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
7042 g.NextWindowData.SizeVal.y = window->SizeFull.y;
7043 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
7044 }
7045 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
7046 {
7047 if (g.NextWindowData.ScrollVal.x >= 0.0f)
7048 {
7049 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
7050 window->ScrollTargetCenterRatio.x = 0.0f;
7051 }
7052 if (g.NextWindowData.ScrollVal.y >= 0.0f)
7053 {
7054 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
7055 window->ScrollTargetCenterRatio.y = 0.0f;
7056 }
7057 }
7058 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
7059 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
7060 else if (first_begin_of_the_frame)
7061 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
7062 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass)
7063 window->WindowClass = g.NextWindowData.WindowClass;
7064 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
7065 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
7066 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
7067 FocusWindow(window);
7068 if (window->Appearing)
7069 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
7070
7071 // [EXPERIMENTAL] Skip Refresh mode
7072 UpdateWindowSkipRefresh(window);
7073
7074 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
7075 g.CurrentWindow = NULL;
7076
7077 // When reusing window again multiple times a frame, just append content (don't need to setup again)
7078 if (first_begin_of_the_frame && !window->SkipRefresh)
7079 {
7080 // Initialize
7081 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
7082 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
7083 window->Active = true;
7084 window->HasCloseButton = (p_open != NULL);
7085 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
7086 window->IDStack.resize(1);
7087 window->DrawList->_ResetForNewFrame();
7088 window->DC.CurrentTableIdx = -1;
7089 if (flags & ImGuiWindowFlags_DockNodeHost)
7090 {
7091 window->DrawList->ChannelsSplit(2);
7092 window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later
7093 }
7094
7095 // Restore buffer capacity when woken from a compacted state, to avoid
7096 if (window->MemoryCompacted)
7097 GcAwakeTransientWindowBuffers(window);
7098
7099 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
7100 // 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.
7101 bool window_title_visible_elsewhere = false;
7102 if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive))
7103 window_title_visible_elsewhere = true;
7104 else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
7105 window_title_visible_elsewhere = true;
7106 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
7107 {
7108 size_t buf_len = (size_t)window->NameBufLen;
7109 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
7110 window->NameBufLen = (int)buf_len;
7111 }
7112
7113 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
7114
7115 // Update contents size from last frame for auto-fitting (or use explicit size)
7116 CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
7117
7118 // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors
7119 // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because
7120 // it has a single usage before this code block and may be set below before it is finally checked.
7121 if (window->HiddenFramesCanSkipItems > 0)
7122 window->HiddenFramesCanSkipItems--;
7123 if (window->HiddenFramesCannotSkipItems > 0)
7124 window->HiddenFramesCannotSkipItems--;
7125 if (window->HiddenFramesForRenderOnly > 0)
7126 window->HiddenFramesForRenderOnly--;
7127
7128 // Hide new windows for one frame until they calculate their size
7129 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
7130 window->HiddenFramesCannotSkipItems = 1;
7131
7132 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
7133 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
7134 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
7135 {
7136 window->HiddenFramesCannotSkipItems = 1;
7137 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
7138 {
7139 if (!window_size_x_set_by_api)
7140 window->Size.x = window->SizeFull.x = 0.f;
7141 if (!window_size_y_set_by_api)
7142 window->Size.y = window->SizeFull.y = 0.f;
7143 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
7144 }
7145 }
7146
7147 // SELECT VIEWPORT
7148 // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes.
7149
7150 WindowSelectViewport(window);
7151 SetCurrentViewport(window, window->Viewport);
7152 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
7153 SetCurrentWindow(window);
7154 flags = window->Flags;
7155
7156 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
7157 // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style.
7158
7159 if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow))
7160 window->WindowBorderSize = style.ChildBorderSize;
7161 else
7162 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
7163 window->WindowPadding = style.WindowPadding;
7164 if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
7165 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
7166
7167 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
7168 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
7169 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
7170 window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
7171 window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
7172
7173 // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
7174 // Those flags will be altered further down in the function depending on more conditions.
7175 bool use_current_size_for_scrollbar_x = window_just_created;
7176 bool use_current_size_for_scrollbar_y = window_just_created;
7177 if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
7178 use_current_size_for_scrollbar_x = true;
7179 if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
7180 use_current_size_for_scrollbar_y = true;
7181
7182 // Collapse window by double-clicking on title bar
7183 // 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
7184 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
7185 {
7186 // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
7187 ImRect title_bar_rect = window->TitleBarRect();
7188 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
7189 if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
7190 window->WantCollapseToggle = true;
7191 if (window->WantCollapseToggle)
7192 {
7193 window->Collapsed = !window->Collapsed;
7194 if (!window->Collapsed)
7195 use_current_size_for_scrollbar_y = true;
7196 MarkIniSettingsDirty(window);
7197 }
7198 }
7199 else
7200 {
7201 window->Collapsed = false;
7202 }
7203 window->WantCollapseToggle = false;
7204
7205 // SIZE
7206
7207 // Outer Decoration Sizes
7208 // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
7209 const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
7210 window->DecoOuterSizeX1 = 0.0f;
7211 window->DecoOuterSizeX2 = 0.0f;
7212 window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
7213 window->DecoOuterSizeY2 = 0.0f;
7214 window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
7215
7216 // Calculate auto-fit size, handle automatic resize
7217 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
7218 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
7219 {
7220 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
7221 if (!window_size_x_set_by_api)
7222 {
7223 window->SizeFull.x = size_auto_fit.x;
7224 use_current_size_for_scrollbar_x = true;
7225 }
7226 if (!window_size_y_set_by_api)
7227 {
7228 window->SizeFull.y = size_auto_fit.y;
7229 use_current_size_for_scrollbar_y = true;
7230 }
7231 }
7232 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7233 {
7234 // Auto-fit may only grow window during the first few frames
7235 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
7236 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
7237 {
7238 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
7239 use_current_size_for_scrollbar_x = true;
7240 }
7241 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
7242 {
7243 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
7244 use_current_size_for_scrollbar_y = true;
7245 }
7246 if (!window->Collapsed)
7247 MarkIniSettingsDirty(window);
7248 }
7249
7250 // Apply minimum/maximum window size constraints and final size
7251 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
7252 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
7253
7254 // POSITION
7255
7256 // Popup latch its initial position, will position itself when it appears next frame
7257 if (window_just_activated_by_user)
7258 {
7259 window->AutoPosLastDirection = ImGuiDir_None;
7260 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
7261 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
7262 }
7263
7264 // Position child window
7265 if (flags & ImGuiWindowFlags_ChildWindow)
7266 {
7267 IM_ASSERT(parent_window && parent_window->Active);
7268 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
7269 parent_window->DC.ChildWindows.push_back(window);
7270 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
7271 window->Pos = parent_window->DC.CursorPos;
7272 }
7273
7274 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
7275 if (window_pos_with_pivot)
7276 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
7277 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
7278 window->Pos = FindBestWindowPosForPopup(window);
7279 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
7280 window->Pos = FindBestWindowPosForPopup(window);
7281 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
7282 window->Pos = FindBestWindowPosForPopup(window);
7283
7284 // Late create viewport if we don't fit within our current host viewport.
7285 if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized))
7286 if (!window->Viewport->GetMainRect().Contains(window->Rect()))
7287 {
7288 // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport)
7289 //ImGuiViewport* old_viewport = window->Viewport;
7290 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
7291
7292 // FIXME-DPI
7293 //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong
7294 SetCurrentViewport(window, window->Viewport);
7295 window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f;
7296 SetCurrentWindow(window);
7297 }
7298
7299 if (window->ViewportOwned)
7300 WindowSyncOwnedViewport(window, parent_window_in_stack);
7301
7302 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
7303 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
7304 ImRect viewport_rect(window->Viewport->GetMainRect());
7305 ImRect viewport_work_rect(window->Viewport->GetWorkRect());
7306 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
7307 ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
7308
7309 // Clamp position/size so window stays visible within its viewport or monitor
7310 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
7311 // FIXME: Similar to code in GetWindowAllowedExtentRect()
7312 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
7313 {
7314 if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
7315 {
7316 ClampWindowPos(window, visibility_rect);
7317 }
7318 else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0)
7319 {
7320 if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree)
7321 {
7322 // While moving windows we allow them to straddle monitors (#7299, #3071)
7323 visibility_rect = g.PlatformMonitorsFullWorkRect;
7324 }
7325 else
7326 {
7327 // When not moving ensure visible in its monitor
7328 // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport.
7329 const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport);
7330 visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize);
7331 }
7332 visibility_rect.Expand(-visibility_padding);
7333 ClampWindowPos(window, visibility_rect);
7334 }
7335 }
7336 window->Pos = ImTrunc(window->Pos);
7337
7338 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
7339 // Large values tend to lead to variety of artifacts and are not recommended.
7340 if (window->ViewportOwned || window->DockIsActive)
7341 window->WindowRounding = 0.0f;
7342 else
7343 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
7344
7345 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
7346 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7347 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
7348
7349 // Apply window focus (new and reactivated windows are moved to front)
7350 bool want_focus = false;
7351 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
7352 {
7353 if (flags & ImGuiWindowFlags_Popup)
7354 want_focus = true;
7355 else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip))
7356 want_focus = true;
7357 }
7358
7359 // [Test Engine] Register whole window in the item system (before submitting further decorations)
7360#ifdef IMGUI_ENABLE_TEST_ENGINE
7361 if (g.TestEngineHookItems)
7362 {
7363 IM_ASSERT(window->IDStack.Size == 1);
7364 window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
7365 IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
7366 IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
7367 window->IDStack.Size = 1;
7368 }
7369#endif
7370
7371 // Decide if we are going to handle borders and resize grips
7372 const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive);
7373
7374 // Handle manual resize: Resize Grips, Borders, Gamepad
7375 int border_hovered = -1, border_held = -1;
7376 ImU32 resize_grip_col[4] = {};
7377 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.
7378 const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
7379 if (handle_borders_and_resize_grips && !window->Collapsed)
7380 if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
7381 {
7382 if (auto_fit_mask & (1 << ImGuiAxis_X))
7383 use_current_size_for_scrollbar_x = true;
7384 if (auto_fit_mask & (1 << ImGuiAxis_Y))
7385 use_current_size_for_scrollbar_y = true;
7386 }
7387 window->ResizeBorderHovered = (signed char)border_hovered;
7388 window->ResizeBorderHeld = (signed char)border_held;
7389
7390 // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either)
7391 if (window->ViewportOwned)
7392 {
7393 if (!window->Viewport->PlatformRequestMove)
7394 window->Viewport->Pos = window->Pos;
7395 if (!window->Viewport->PlatformRequestResize)
7396 window->Viewport->Size = window->Size;
7397 window->Viewport->UpdateWorkRect();
7398 viewport_rect = window->Viewport->GetMainRect();
7399 }
7400
7401 // Save last known viewport position within the window itself (so it can be saved in .ini file and restored)
7402 window->ViewportPos = window->Viewport->Pos;
7403
7404 // SCROLLBAR VISIBILITY
7405
7406 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
7407 if (!window->Collapsed)
7408 {
7409 // When reading the current size we need to read it after size constraints have been applied.
7410 // Intentionally use previous frame values for InnerRect and ScrollbarSizes.
7411 // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
7412 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
7413 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
7414 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
7415 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
7416 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
7417 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
7418 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
7419 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));
7420 if (window->ScrollbarX && !window->ScrollbarY)
7421 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
7422 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
7423
7424 // Amend the partially filled window->DecorationXXX values.
7425 window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
7426 window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
7427 }
7428
7429 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
7430 // Update various regions. Variables they depend on should be set above in this function.
7431 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
7432
7433 // Outer rectangle
7434 // Not affected by window border size. Used by:
7435 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
7436 // - Begin() initial clipping rect for drawing window background and borders.
7437 // - Begin() clipping whole child
7438 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
7439 const ImRect outer_rect = window->Rect();
7440 const ImRect title_bar_rect = window->TitleBarRect();
7441 window->OuterRectClipped = outer_rect;
7442 if (window->DockIsActive)
7443 window->OuterRectClipped.Min.y += window->TitleBarHeight;
7444 window->OuterRectClipped.ClipWith(host_rect);
7445
7446 // Inner rectangle
7447 // Not affected by window border size. Used by:
7448 // - InnerClipRect
7449 // - ScrollToRectEx()
7450 // - NavUpdatePageUpPageDown()
7451 // - Scrollbar()
7452 window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
7453 window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
7454 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
7455 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
7456
7457 // Inner clipping rectangle.
7458 // - Extend a outside of normal work region up to borders.
7459 // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
7460 // - It also makes clipped items be more noticeable.
7461 // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
7462 // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
7463 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
7464 // Affected by window/frame border size. Used by:
7465 // - Begin() initial clip rect
7466 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
7467 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize);
7468 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
7469 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - window->WindowBorderSize);
7470 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
7471 window->InnerClipRect.ClipWithFull(host_rect);
7472
7473 // Default item width. Make it proportional to window size if window manually resizes
7474 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
7475 window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
7476 else
7477 window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
7478
7479 // SCROLLING
7480
7481 // Lock down maximum scrolling
7482 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
7483 // for right/bottom aligned items without creating a scrollbar.
7484 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
7485 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
7486
7487 // Apply scrolling
7488 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7489 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
7490 window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
7491
7492 // DRAWING
7493
7494 // Setup draw list and outer clipping rectangle
7495 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
7496 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
7497 PushClipRect(host_rect.Min, host_rect.Max, false);
7498
7499 // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
7500 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
7501 // 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)
7502 const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible;
7503 if (is_undocked_or_docked_visible)
7504 {
7505 bool render_decorations_in_parent = false;
7506 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
7507 {
7508 // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
7509 // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
7510 ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
7511 bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
7512 bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
7513 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
7514 render_decorations_in_parent = true;
7515 }
7516 if (render_decorations_in_parent)
7517 window->DrawList = parent_window->DrawList;
7518
7519 // Handle title bar, scrollbar, resize grips and resize borders
7520 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
7521 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)));
7522 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
7523
7524 if (render_decorations_in_parent)
7525 window->DrawList = &window->DrawListInst;
7526 }
7527
7528 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
7529
7530 // Work rectangle.
7531 // Affected by window padding and border size. Used by:
7532 // - Columns() for right-most edge
7533 // - TreeNode(), CollapsingHeader() for right-most edge
7534 // - BeginTabBar() for right-most edge
7535 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
7536 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
7537 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)));
7538 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)));
7539 window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
7540 window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
7541 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
7542 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
7543 window->ParentWorkRect = window->WorkRect;
7544
7545 // [LEGACY] Content Region
7546 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
7547 // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
7548 // Used by:
7549 // - Mouse wheel scrolling + many other things
7550 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
7551 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
7552 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)));
7553 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)));
7554
7555 // Setup drawing context
7556 // (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.)
7557 window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
7558 window->DC.GroupOffset.x = 0.0f;
7559 window->DC.ColumnsOffset.x = 0.0f;
7560
7561 // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
7562 // 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.
7563 double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
7564 double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
7565 window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
7566 window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
7567 window->DC.CursorPos = window->DC.CursorStartPos;
7568 window->DC.CursorPosPrevLine = window->DC.CursorPos;
7569 window->DC.CursorMaxPos = window->DC.CursorStartPos;
7570 window->DC.IdealMaxPos = window->DC.CursorStartPos;
7571 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
7572 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
7573 window->DC.IsSameLine = window->DC.IsSetPos = false;
7574
7575 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7576 window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
7577 window->DC.NavLayersActiveMaskNext = 0x00;
7578 window->DC.NavIsScrollPushableX = true;
7579 window->DC.NavHideHighlightOneFrame = false;
7580 window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
7581
7582 window->DC.MenuBarAppending = false;
7583 window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
7584 window->DC.TreeDepth = 0;
7585 window->DC.TreeJumpToParentOnPopMask = 0x00;
7586 window->DC.ChildWindows.resize(0);
7587 window->DC.StateStorage = &window->StateStorage;
7588 window->DC.CurrentColumns = NULL;
7589 window->DC.LayoutType = ImGuiLayoutType_Vertical;
7590 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
7591
7592 window->DC.ItemWidth = window->ItemWidthDefault;
7593 window->DC.TextWrapPos = -1.0f; // disabled
7594 window->DC.ItemWidthStack.resize(0);
7595 window->DC.TextWrapPosStack.resize(0);
7596 if (flags & ImGuiWindowFlags_Modal)
7597 window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
7598
7599 if (window->AutoFitFramesX > 0)
7600 window->AutoFitFramesX--;
7601 if (window->AutoFitFramesY > 0)
7602 window->AutoFitFramesY--;
7603
7604 // Clear SetNextWindowXXX data (can aim to move this higher in the function)
7605 g.NextWindowData.ClearFlags();
7606
7607 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
7608 // We ImGuiFocusRequestFlags_UnlessBelowModal to:
7609 // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
7610 // - Position window behind the modal that is not a begin-parent of this window.
7611 if (want_focus)
7612 FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
7613 if (want_focus && window == g.NavWindow)
7614 NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
7615
7616 // Close requested by platform window (apply to all windows in this viewport)
7617 if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport())
7618 {
7619 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name);
7620 *p_open = false;
7621 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.
7622 }
7623
7624 // Title bar
7625 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
7626 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);
7627
7628 // Clear hit test shape every frame
7629 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
7630
7631 // Pressing CTRL+C while holding on a window copy its content to the clipboard
7632 // 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.
7633 // Maybe we can support CTRL+C on every element?
7634 /*
7635 //if (g.NavWindow == window && g.ActiveId == 0)
7636 if (g.ActiveId == window->MoveId)
7637 if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))
7638 LogToClipboard();
7639 */
7640
7641 if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
7642 {
7643 // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
7644 // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it.
7645 if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0)
7646 BeginDockableDragDropSource(window);
7647
7648 // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead.
7649 if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
7650 if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window)
7651 if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
7652 BeginDockableDragDropTarget(window);
7653 }
7654
7655 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
7656 // This is useful to allow creating context menus on title bar only, etc.
7657 SetLastItemDataForWindow(window, title_bar_rect);
7658
7659 // [DEBUG]
7660#ifndef IMGUI_DISABLE_DEBUG_TOOLS
7661 if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
7662 DebugLocateItemResolveWithLastItem();
7663#endif
7664
7665 // [Test Engine] Register title bar / tab with MoveId.
7666#ifdef IMGUI_ENABLE_TEST_ENGINE
7667 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
7668 IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
7669#endif
7670 }
7671 else
7672 {
7673 // Skip refresh always mark active
7674 if (window->SkipRefresh)
7675 window->Active = true;
7676
7677 // Append
7678 SetCurrentViewport(window, window->Viewport);
7679 SetCurrentWindow(window);
7680 g.NextWindowData.ClearFlags();
7681 SetLastItemDataForWindow(window, window->TitleBarRect());
7682 }
7683
7684 if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh)
7685 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
7686
7687 // 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)
7688 window->WriteAccessed = false;
7689 window->BeginCount++;
7690
7691 // Update visibility
7692 if (first_begin_of_the_frame && !window->SkipRefresh)
7693 {
7694 // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems.
7695 // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents.
7696 // This is analogous to regular windows being hidden from one frame.
7697 // 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.
7698 if (window->DockIsActive && !window->DockTabIsVisible)
7699 {
7700 if (window->LastFrameJustFocused == g.FrameCount)
7701 window->HiddenFramesCannotSkipItems = 1;
7702 else
7703 window->HiddenFramesCanSkipItems = 1;
7704 }
7705
7706 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
7707 {
7708 // Child window can be out of sight and have "negative" clip windows.
7709 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
7710 IM_ASSERT((flags& ImGuiWindowFlags_NoTitleBar) != 0 || window->DockIsActive);
7711 const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
7712 if (!g.LogEnabled && !nav_request)
7713 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
7714 {
7715 if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7716 window->HiddenFramesCannotSkipItems = 1;
7717 else
7718 window->HiddenFramesCanSkipItems = 1;
7719 }
7720
7721 // Hide along with parent or if parent is collapsed
7722 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
7723 window->HiddenFramesCanSkipItems = 1;
7724 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
7725 window->HiddenFramesCannotSkipItems = 1;
7726 }
7727
7728 // 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)
7729 if (style.Alpha <= 0.0f)
7730 window->HiddenFramesCanSkipItems = 1;
7731
7732 // Update the Hidden flag
7733 bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
7734 window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
7735
7736 // Disable inputs for requested number of frames
7737 if (window->DisableInputsFrames > 0)
7738 {
7739 window->DisableInputsFrames--;
7740 window->Flags |= ImGuiWindowFlags_NoInputs;
7741 }
7742
7743 // Update the SkipItems flag, used to early out of all items functions (no layout required)
7744 bool skip_items = false;
7745 if (window->Collapsed || !window->Active || hidden_regular)
7746 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
7747 skip_items = true;
7748 window->SkipItems = skip_items;
7749
7750 // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value.
7751 if (window->SkipItems)
7752 window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask;
7753
7754 // Sanity check: there are two spots which can set Appearing = true
7755 // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false
7756 // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert.
7757 if (window->SkipItems && !window->Appearing)
7758 IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177
7759 }
7760 else if (first_begin_of_the_frame)
7761 {
7762 // Skip refresh mode
7763 window->SkipItems = true;
7764 }
7765
7766 // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
7767 // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
7768#ifndef IMGUI_DISABLE_DEBUG_TOOLS
7769 if (!window->IsFallbackWindow)
7770 if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
7771 {
7772 if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
7773 if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
7774 return false;
7775 }
7776#endif
7777
7778 return !window->SkipItems;
7779}
7780
7781static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
7782{
7783 ImGuiContext& g = *GImGui;
7784 if (window->DockIsActive)
7785 SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DockTabItemStatusFlags, window->DockTabItemRect);
7786 else
7787 SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect);
7788}
7789
7790void ImGui::End()
7791{
7792 ImGuiContext& g = *GImGui;
7793 ImGuiWindow* window = g.CurrentWindow;
7794
7795 // Error checking: verify that user hasn't called End() too many times!
7796 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
7797 {
7798 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
7799 return;
7800 }
7801 IM_ASSERT(g.CurrentWindowStack.Size > 0);
7802
7803 // Error checking: verify that user doesn't directly call End() on a child window.
7804 if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive)
7805 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
7806
7807 // Close anything that is open
7808 if (window->DC.CurrentColumns)
7809 EndColumns();
7810 if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) // Pop inner window clip rectangle
7811 PopClipRect();
7812 PopFocusScope();
7813
7814 if (window->SkipRefresh)
7815 {
7816 IM_ASSERT(window->DrawList == NULL);
7817 window->DrawList = &window->DrawListInst;
7818 }
7819
7820 // Stop logging
7821 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
7822 LogFinish();
7823
7824 if (window->DC.IsSetPos)
7825 ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
7826
7827 // Docking: report contents sizes to parent to allow for auto-resize
7828 if (window->DockNode && window->DockTabIsVisible)
7829 if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK
7830 host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
7831
7832 // Pop from window stack
7833 g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
7834 if (window->Flags & ImGuiWindowFlags_ChildMenu)
7835 g.BeginMenuDepth--;
7836 if (window->Flags & ImGuiWindowFlags_Popup)
7837 g.BeginPopupStack.pop_back();
7838 g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithContextState(&g);
7839 g.CurrentWindowStack.pop_back();
7840 SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
7841 if (g.CurrentWindow)
7842 SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport);
7843}
7844
7845void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
7846{
7847 ImGuiContext& g = *GImGui;
7848 IM_ASSERT(window == window->RootWindow);
7849
7850 const int cur_order = window->FocusOrder;
7851 IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
7852 if (g.WindowsFocusOrder.back() == window)
7853 return;
7854
7855 const int new_order = g.WindowsFocusOrder.Size - 1;
7856 for (int n = cur_order; n < new_order; n++)
7857 {
7858 g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
7859 g.WindowsFocusOrder[n]->FocusOrder--;
7860 IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
7861 }
7862 g.WindowsFocusOrder[new_order] = window;
7863 window->FocusOrder = (short)new_order;
7864}
7865
7866void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
7867{
7868 ImGuiContext& g = *GImGui;
7869 ImGuiWindow* current_front_window = g.Windows.back();
7870 if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better)
7871 return;
7872 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
7873 if (g.Windows[i] == window)
7874 {
7875 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
7876 g.Windows[g.Windows.Size - 1] = window;
7877 break;
7878 }
7879}
7880
7881void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
7882{
7883 ImGuiContext& g = *GImGui;
7884 if (g.Windows[0] == window)
7885 return;
7886 for (int i = 0; i < g.Windows.Size; i++)
7887 if (g.Windows[i] == window)
7888 {
7889 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
7890 g.Windows[0] = window;
7891 break;
7892 }
7893}
7894
7895void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
7896{
7897 IM_ASSERT(window != NULL && behind_window != NULL);
7898 ImGuiContext& g = *GImGui;
7899 window = window->RootWindow;
7900 behind_window = behind_window->RootWindow;
7901 int pos_wnd = FindWindowDisplayIndex(window);
7902 int pos_beh = FindWindowDisplayIndex(behind_window);
7903 if (pos_wnd < pos_beh)
7904 {
7905 size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
7906 memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
7907 g.Windows[pos_beh - 1] = window;
7908 }
7909 else
7910 {
7911 size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
7912 memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
7913 g.Windows[pos_beh] = window;
7914 }
7915}
7916
7917int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
7918{
7919 ImGuiContext& g = *GImGui;
7920 return g.Windows.index_from_ptr(g.Windows.find(window));
7921}
7922
7923// Moving window to front of display and set focus (which happens to be back of our sorted list)
7924void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
7925{
7926 ImGuiContext& g = *GImGui;
7927
7928 // Modal check?
7929 if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
7930 if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
7931 {
7932 IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
7933 if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
7934 BringWindowToDisplayBehind(window, blocking_modal); // Still bring to right below modal.
7935 return;
7936 }
7937
7938 // Find last focused child (if any) and focus it instead.
7939 if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
7940 window = NavRestoreLastChildNavWindow(window);
7941
7942 // Apply focus
7943 if (g.NavWindow != window)
7944 {
7945 SetNavWindow(window);
7946 if (window && g.NavDisableMouseHover)
7947 g.NavMousePosDirty = true;
7948 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
7949 g.NavLayer = ImGuiNavLayer_Main;
7950 SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
7951 g.NavIdIsAlive = false;
7952 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
7953
7954 // Close popups if any
7955 ClosePopupsOverWindow(window, false);
7956 }
7957
7958 // Move the root window to the top of the pile
7959 IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL);
7960 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL;
7961 ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL;
7962 ImGuiDockNode* dock_node = window ? window->DockNode : NULL;
7963 bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow);
7964
7965 // Steal active widgets. Some of the cases it triggers includes:
7966 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
7967 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
7968 // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window.
7969 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
7970 if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host)
7971 ClearActiveID();
7972
7973 // Passing NULL allow to disable keyboard focus
7974 if (!window)
7975 return;
7976 window->LastFrameJustFocused = g.FrameCount;
7977
7978 // Select in dock node
7979 // For #2304 we avoid applying focus immediately before the tabbar is visible.
7980 //if (dock_node && dock_node->TabBar)
7981 // dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId;
7982
7983 // Bring to front
7984 BringWindowToFocusFront(focus_front_window);
7985 if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
7986 BringWindowToDisplayFront(display_front_window);
7987}
7988
7989void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
7990{
7991 ImGuiContext& g = *GImGui;
7992 int start_idx = g.WindowsFocusOrder.Size - 1;
7993 if (under_this_window != NULL)
7994 {
7995 // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
7996 int offset = -1;
7997 while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
7998 {
7999 under_this_window = under_this_window->ParentWindow;
8000 offset = 0;
8001 }
8002 start_idx = FindWindowFocusIndex(under_this_window) + offset;
8003 }
8004 for (int i = start_idx; i >= 0; i--)
8005 {
8006 // 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.
8007 ImGuiWindow* window = g.WindowsFocusOrder[i];
8008 if (window == ignore_window || !window->WasActive)
8009 continue;
8010 if (filter_viewport != NULL && window->Viewport != filter_viewport)
8011 continue;
8012 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
8013 {
8014 // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set...
8015 // This is failing (lagging by one frame) for docked windows.
8016 // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B.
8017 // 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)
8018 // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself?
8019 FocusWindow(window, flags);
8020 return;
8021 }
8022 }
8023 FocusWindow(NULL, flags);
8024}
8025
8026// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
8027void ImGui::SetCurrentFont(ImFont* font)
8028{
8029 ImGuiContext& g = *GImGui;
8030 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
8031 IM_ASSERT(font->Scale > 0.0f);
8032 g.Font = font;
8033 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
8034 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
8035
8036 ImFontAtlas* atlas = g.Font->ContainerAtlas;
8037 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
8038 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
8039 g.DrawListSharedData.Font = g.Font;
8040 g.DrawListSharedData.FontSize = g.FontSize;
8041}
8042
8043void ImGui::PushFont(ImFont* font)
8044{
8045 ImGuiContext& g = *GImGui;
8046 if (!font)
8047 font = GetDefaultFont();
8048 SetCurrentFont(font);
8049 g.FontStack.push_back(font);
8050 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
8051}
8052
8053void ImGui::PopFont()
8054{
8055 ImGuiContext& g = *GImGui;
8056 g.CurrentWindow->DrawList->PopTextureID();
8057 g.FontStack.pop_back();
8058 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
8059}
8060
8061void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
8062{
8063 ImGuiContext& g = *GImGui;
8064 ImGuiItemFlags item_flags = g.CurrentItemFlags;
8065 IM_ASSERT(item_flags == g.ItemFlagsStack.back());
8066 if (enabled)
8067 item_flags |= option;
8068 else
8069 item_flags &= ~option;
8070 g.CurrentItemFlags = item_flags;
8071 g.ItemFlagsStack.push_back(item_flags);
8072}
8073
8074void ImGui::PopItemFlag()
8075{
8076 ImGuiContext& g = *GImGui;
8077 IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
8078 g.ItemFlagsStack.pop_back();
8079 g.CurrentItemFlags = g.ItemFlagsStack.back();
8080}
8081
8082// BeginDisabled()/EndDisabled()
8083// - 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)
8084// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
8085// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
8086// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
8087// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
8088void ImGui::BeginDisabled(bool disabled)
8089{
8090 ImGuiContext& g = *GImGui;
8091 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8092 if (!was_disabled && disabled)
8093 {
8094 g.DisabledAlphaBackup = g.Style.Alpha;
8095 g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
8096 }
8097 if (was_disabled || disabled)
8098 g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
8099 g.ItemFlagsStack.push_back(g.CurrentItemFlags);
8100 g.DisabledStackSize++;
8101}
8102
8103void ImGui::EndDisabled()
8104{
8105 ImGuiContext& g = *GImGui;
8106 IM_ASSERT(g.DisabledStackSize > 0);
8107 g.DisabledStackSize--;
8108 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8109 //PopItemFlag();
8110 g.ItemFlagsStack.pop_back();
8111 g.CurrentItemFlags = g.ItemFlagsStack.back();
8112 if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
8113 g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
8114}
8115
8116void ImGui::PushTabStop(bool tab_stop)
8117{
8118 PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop);
8119}
8120
8121void ImGui::PopTabStop()
8122{
8123 PopItemFlag();
8124}
8125
8126void ImGui::PushButtonRepeat(bool repeat)
8127{
8128 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
8129}
8130
8131void ImGui::PopButtonRepeat()
8132{
8133 PopItemFlag();
8134}
8135
8136void ImGui::PushTextWrapPos(float wrap_pos_x)
8137{
8138 ImGuiWindow* window = GetCurrentWindow();
8139 window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
8140 window->DC.TextWrapPos = wrap_pos_x;
8141}
8142
8143void ImGui::PopTextWrapPos()
8144{
8145 ImGuiWindow* window = GetCurrentWindow();
8146 window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
8147 window->DC.TextWrapPosStack.pop_back();
8148}
8149
8150static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
8151{
8152 ImGuiWindow* last_window = NULL;
8153 while (last_window != window)
8154 {
8155 last_window = window;
8156 window = window->RootWindow;
8157 if (popup_hierarchy)
8158 window = window->RootWindowPopupTree;
8159 if (dock_hierarchy)
8160 window = window->RootWindowDockTree;
8161 }
8162 return window;
8163}
8164
8165bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
8166{
8167 ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
8168 if (window_root == potential_parent)
8169 return true;
8170 while (window != NULL)
8171 {
8172 if (window == potential_parent)
8173 return true;
8174 if (window == window_root) // end of chain
8175 return false;
8176 window = window->ParentWindow;
8177 }
8178 return false;
8179}
8180
8181bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
8182{
8183 if (window->RootWindow == potential_parent)
8184 return true;
8185 while (window != NULL)
8186 {
8187 if (window == potential_parent)
8188 return true;
8189 window = window->ParentWindowInBeginStack;
8190 }
8191 return false;
8192}
8193
8194bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
8195{
8196 ImGuiContext& g = *GImGui;
8197
8198 // 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
8199 const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
8200 if (display_layer_delta != 0)
8201 return display_layer_delta > 0;
8202
8203 for (int i = g.Windows.Size - 1; i >= 0; i--)
8204 {
8205 ImGuiWindow* candidate_window = g.Windows[i];
8206 if (candidate_window == potential_above)
8207 return true;
8208 if (candidate_window == potential_below)
8209 return false;
8210 }
8211 return false;
8212}
8213
8214// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
8215// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
8216// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
8217// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
8218bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
8219{
8220 IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!");
8221
8222 ImGuiContext& g = *GImGui;
8223 ImGuiWindow* ref_window = g.HoveredWindow;
8224 ImGuiWindow* cur_window = g.CurrentWindow;
8225 if (ref_window == NULL)
8226 return false;
8227
8228 if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
8229 {
8230 IM_ASSERT(cur_window); // Not inside a Begin()/End()
8231 const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
8232 const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;
8233 if (flags & ImGuiHoveredFlags_RootWindow)
8234 cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
8235
8236 bool result;
8237 if (flags & ImGuiHoveredFlags_ChildWindows)
8238 result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
8239 else
8240 result = (ref_window == cur_window);
8241 if (!result)
8242 return false;
8243 }
8244
8245 if (!IsWindowContentHoverable(ref_window, flags))
8246 return false;
8247 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
8248 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
8249 return false;
8250
8251 // When changing hovered window we requires a bit of stationary delay before activating hover timer.
8252 // FIXME: We don't support delay other than stationary one for now, other delay would need a way
8253 // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
8254 // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
8255 // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
8256 if (flags & ImGuiHoveredFlags_ForTooltip)
8257 flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
8258 if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
8259 return false;
8260
8261 return true;
8262}
8263
8264bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
8265{
8266 ImGuiContext& g = *GImGui;
8267 ImGuiWindow* ref_window = g.NavWindow;
8268 ImGuiWindow* cur_window = g.CurrentWindow;
8269
8270 if (ref_window == NULL)
8271 return false;
8272 if (flags & ImGuiFocusedFlags_AnyWindow)
8273 return true;
8274
8275 IM_ASSERT(cur_window); // Not inside a Begin()/End()
8276 const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
8277 const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;
8278 if (flags & ImGuiHoveredFlags_RootWindow)
8279 cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);
8280
8281 if (flags & ImGuiHoveredFlags_ChildWindows)
8282 return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
8283 else
8284 return (ref_window == cur_window);
8285}
8286
8287ImGuiID ImGui::GetWindowDockID()
8288{
8289 ImGuiContext& g = *GImGui;
8290 return g.CurrentWindow->DockId;
8291}
8292
8293bool ImGui::IsWindowDocked()
8294{
8295 ImGuiContext& g = *GImGui;
8296 return g.CurrentWindow->DockIsActive;
8297}
8298
8299// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
8300// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
8301// If you want a window to never be focused, you may use the e.g. NoInputs flag.
8302bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
8303{
8304 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
8305}
8306
8307float ImGui::GetWindowWidth()
8308{
8309 ImGuiWindow* window = GImGui->CurrentWindow;
8310 return window->Size.x;
8311}
8312
8313float ImGui::GetWindowHeight()
8314{
8315 ImGuiWindow* window = GImGui->CurrentWindow;
8316 return window->Size.y;
8317}
8318
8319ImVec2 ImGui::GetWindowPos()
8320{
8321 ImGuiContext& g = *GImGui;
8322 ImGuiWindow* window = g.CurrentWindow;
8323 return window->Pos;
8324}
8325
8326void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
8327{
8328 // Test condition (NB: bit 0 is always true) and clear flags for next time
8329 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
8330 return;
8331
8332 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8333 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8334 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
8335
8336 // Set
8337 const ImVec2 old_pos = window->Pos;
8338 window->Pos = ImTrunc(pos);
8339 ImVec2 offset = window->Pos - old_pos;
8340 if (offset.x == 0.0f && offset.y == 0.0f)
8341 return;
8342 MarkIniSettingsDirty(window);
8343 // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here.
8344 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
8345 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
8346 window->DC.IdealMaxPos += offset;
8347 window->DC.CursorStartPos += offset;
8348}
8349
8350void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
8351{
8352 ImGuiWindow* window = GetCurrentWindowRead();
8353 SetWindowPos(window, pos, cond);
8354}
8355
8356void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
8357{
8358 if (ImGuiWindow* window = FindWindowByName(name))
8359 SetWindowPos(window, pos, cond);
8360}
8361
8362ImVec2 ImGui::GetWindowSize()
8363{
8364 ImGuiWindow* window = GetCurrentWindowRead();
8365 return window->Size;
8366}
8367
8368void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
8369{
8370 // Test condition (NB: bit 0 is always true) and clear flags for next time
8371 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
8372 return;
8373
8374 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8375 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8376
8377 // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
8378 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8379 window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
8380 if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8381 window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
8382
8383 // Set
8384 ImVec2 old_size = window->SizeFull;
8385 if (size.x <= 0.0f)
8386 window->AutoFitOnlyGrows = false;
8387 else
8388 window->SizeFull.x = IM_TRUNC(size.x);
8389 if (size.y <= 0.0f)
8390 window->AutoFitOnlyGrows = false;
8391 else
8392 window->SizeFull.y = IM_TRUNC(size.y);
8393 if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
8394 MarkIniSettingsDirty(window);
8395}
8396
8397void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
8398{
8399 SetWindowSize(GImGui->CurrentWindow, size, cond);
8400}
8401
8402void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
8403{
8404 if (ImGuiWindow* window = FindWindowByName(name))
8405 SetWindowSize(window, size, cond);
8406}
8407
8408void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
8409{
8410 // Test condition (NB: bit 0 is always true) and clear flags for next time
8411 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
8412 return;
8413 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8414
8415 // Set
8416 window->Collapsed = collapsed;
8417}
8418
8419void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
8420{
8421 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
8422 window->HitTestHoleSize = ImVec2ih(size);
8423 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
8424}
8425
8426void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
8427{
8428 window->Hidden = window->SkipItems = true;
8429 window->HiddenFramesCanSkipItems = 1;
8430}
8431
8432void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
8433{
8434 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
8435}
8436
8437bool ImGui::IsWindowCollapsed()
8438{
8439 ImGuiWindow* window = GetCurrentWindowRead();
8440 return window->Collapsed;
8441}
8442
8443bool ImGui::IsWindowAppearing()
8444{
8445 ImGuiWindow* window = GetCurrentWindowRead();
8446 return window->Appearing;
8447}
8448
8449void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
8450{
8451 if (ImGuiWindow* window = FindWindowByName(name))
8452 SetWindowCollapsed(window, collapsed, cond);
8453}
8454
8455void ImGui::SetWindowFocus()
8456{
8457 FocusWindow(GImGui->CurrentWindow);
8458}
8459
8460void ImGui::SetWindowFocus(const char* name)
8461{
8462 if (name)
8463 {
8464 if (ImGuiWindow* window = FindWindowByName(name))
8465 FocusWindow(window);
8466 }
8467 else
8468 {
8469 FocusWindow(NULL);
8470 }
8471}
8472
8473void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
8474{
8475 ImGuiContext& g = *GImGui;
8476 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8477 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
8478 g.NextWindowData.PosVal = pos;
8479 g.NextWindowData.PosPivotVal = pivot;
8480 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
8481 g.NextWindowData.PosUndock = true;
8482}
8483
8484void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
8485{
8486 ImGuiContext& g = *GImGui;
8487 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8488 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
8489 g.NextWindowData.SizeVal = size;
8490 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
8491}
8492
8493// For each axis:
8494// - 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.
8495// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
8496// - See "Demo->Examples->Constrained-resizing window" for examples.
8497void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
8498{
8499 ImGuiContext& g = *GImGui;
8500 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
8501 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
8502 g.NextWindowData.SizeCallback = custom_callback;
8503 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
8504}
8505
8506// Content size = inner scrollable rectangle, padded with WindowPadding.
8507// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
8508void ImGui::SetNextWindowContentSize(const ImVec2& size)
8509{
8510 ImGuiContext& g = *GImGui;
8511 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
8512 g.NextWindowData.ContentSizeVal = ImTrunc(size);
8513}
8514
8515void ImGui::SetNextWindowScroll(const ImVec2& scroll)
8516{
8517 ImGuiContext& g = *GImGui;
8518 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
8519 g.NextWindowData.ScrollVal = scroll;
8520}
8521
8522void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
8523{
8524 ImGuiContext& g = *GImGui;
8525 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8526 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
8527 g.NextWindowData.CollapsedVal = collapsed;
8528 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
8529}
8530
8531void ImGui::SetNextWindowFocus()
8532{
8533 ImGuiContext& g = *GImGui;
8534 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
8535}
8536
8537void ImGui::SetNextWindowBgAlpha(float alpha)
8538{
8539 ImGuiContext& g = *GImGui;
8540 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
8541 g.NextWindowData.BgAlphaVal = alpha;
8542}
8543
8544void ImGui::SetNextWindowViewport(ImGuiID id)
8545{
8546 ImGuiContext& g = *GImGui;
8547 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport;
8548 g.NextWindowData.ViewportId = id;
8549}
8550
8551void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond)
8552{
8553 ImGuiContext& g = *GImGui;
8554 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock;
8555 g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
8556 g.NextWindowData.DockId = id;
8557}
8558
8559void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class)
8560{
8561 ImGuiContext& g = *GImGui;
8562 IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit
8563 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass;
8564 g.NextWindowData.WindowClass = *window_class;
8565}
8566
8567// This is experimental and meant to be a toy for exploring a future/wider range of features.
8568void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
8569{
8570 ImGuiContext& g = *GImGui;
8571 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
8572 g.NextWindowData.RefreshFlagsVal = flags;
8573}
8574
8575ImDrawList* ImGui::GetWindowDrawList()
8576{
8577 ImGuiWindow* window = GetCurrentWindow();
8578 return window->DrawList;
8579}
8580
8581float ImGui::GetWindowDpiScale()
8582{
8583 ImGuiContext& g = *GImGui;
8584 return g.CurrentDpiScale;
8585}
8586
8587ImGuiViewport* ImGui::GetWindowViewport()
8588{
8589 ImGuiContext& g = *GImGui;
8590 IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport);
8591 return g.CurrentViewport;
8592}
8593
8594ImFont* ImGui::GetFont()
8595{
8596 return GImGui->Font;
8597}
8598
8599float ImGui::GetFontSize()
8600{
8601 return GImGui->FontSize;
8602}
8603
8604ImVec2 ImGui::GetFontTexUvWhitePixel()
8605{
8606 return GImGui->DrawListSharedData.TexUvWhitePixel;
8607}
8608
8609void ImGui::SetWindowFontScale(float scale)
8610{
8611 IM_ASSERT(scale > 0.0f);
8612 ImGuiContext& g = *GImGui;
8613 ImGuiWindow* window = GetCurrentWindow();
8614 window->FontWindowScale = scale;
8615 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
8616}
8617
8618void ImGui::PushFocusScope(ImGuiID id)
8619{
8620 ImGuiContext& g = *GImGui;
8621 ImGuiFocusScopeData data;
8622 data.ID = id;
8623 data.WindowID = g.CurrentWindow->ID;
8624 g.FocusScopeStack.push_back(data);
8625 g.CurrentFocusScopeId = id;
8626}
8627
8628void ImGui::PopFocusScope()
8629{
8630 ImGuiContext& g = *GImGui;
8631 if (g.FocusScopeStack.Size == 0)
8632 {
8633 IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!");
8634 return;
8635 }
8636 g.FocusScopeStack.pop_back();
8637 g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
8638}
8639
8640void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
8641{
8642 ImGuiContext& g = *GImGui;
8643 g.NavFocusScopeId = focus_scope_id;
8644 g.NavFocusRoute.resize(0); // Invalidate
8645 if (focus_scope_id == 0)
8646 return;
8647 IM_ASSERT(g.NavWindow != NULL);
8648
8649 // Store current path (in reverse order)
8650 if (focus_scope_id == g.CurrentFocusScopeId)
8651 {
8652 // Top of focus stack contains local focus scopes inside current window
8653 for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
8654 g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
8655 }
8656 else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
8657 g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
8658 else
8659 return;
8660
8661 // Then follow on manually set ParentWindowForFocusRoute field (#6798)
8662 for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
8663 g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
8664 IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
8665}
8666
8667// Focus = move navigation cursor, set scrolling, set focus window.
8668void ImGui::FocusItem()
8669{
8670 ImGuiContext& g = *GImGui;
8671 ImGuiWindow* window = g.CurrentWindow;
8672 IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
8673 if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
8674 {
8675 IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
8676 return;
8677 }
8678
8679 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight | ImGuiNavMoveFlags_NoSelect;
8680 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8681 SetNavWindow(window);
8682 NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
8683 NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8684}
8685
8686void ImGui::ActivateItemByID(ImGuiID id)
8687{
8688 ImGuiContext& g = *GImGui;
8689 g.NavNextActivateId = id;
8690 g.NavNextActivateFlags = ImGuiActivateFlags_None;
8691}
8692
8693// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
8694// But ActivateItem() should function without altering scroll/focus?
8695void ImGui::SetKeyboardFocusHere(int offset)
8696{
8697 ImGuiContext& g = *GImGui;
8698 ImGuiWindow* window = g.CurrentWindow;
8699 IM_ASSERT(offset >= -1); // -1 is allowed but not below
8700 IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
8701
8702 // It makes sense in the vast majority of cases to never interrupt a drag and drop.
8703 // When we refactor this function into ActivateItem() we may want to make this an option.
8704 // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
8705 // is also automatically dropped in the event g.ActiveId is stolen.
8706 if (g.DragDropActive || g.MovingWindow != NULL)
8707 {
8708 IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
8709 return;
8710 }
8711
8712 SetNavWindow(window);
8713
8714 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight;
8715 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8716 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.
8717 if (offset == -1)
8718 {
8719 NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8720 }
8721 else
8722 {
8723 g.NavTabbingDir = 1;
8724 g.NavTabbingCounter = offset + 1;
8725 }
8726}
8727
8728void ImGui::SetItemDefaultFocus()
8729{
8730 ImGuiContext& g = *GImGui;
8731 ImGuiWindow* window = g.CurrentWindow;
8732 if (!window->Appearing)
8733 return;
8734 if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
8735 return;
8736
8737 g.NavInitRequest = false;
8738 NavApplyItemToResult(&g.NavInitResult);
8739 NavUpdateAnyRequestFlag();
8740
8741 // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
8742 if (!window->ClipRect.Contains(g.LastItemData.Rect))
8743 ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
8744}
8745
8746void ImGui::SetStateStorage(ImGuiStorage* tree)
8747{
8748 ImGuiWindow* window = GImGui->CurrentWindow;
8749 window->DC.StateStorage = tree ? tree : &window->StateStorage;
8750}
8751
8752ImGuiStorage* ImGui::GetStateStorage()
8753{
8754 ImGuiWindow* window = GImGui->CurrentWindow;
8755 return window->DC.StateStorage;
8756}
8757
8758bool ImGui::IsRectVisible(const ImVec2& size)
8759{
8760 ImGuiWindow* window = GImGui->CurrentWindow;
8761 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
8762}
8763
8764bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
8765{
8766 ImGuiWindow* window = GImGui->CurrentWindow;
8767 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
8768}
8769
8770//-----------------------------------------------------------------------------
8771// [SECTION] ID STACK
8772//-----------------------------------------------------------------------------
8773
8774// This is one of the very rare legacy case where we use ImGuiWindow methods,
8775// it should ideally be flattened at some point but it's been used a lots by widgets.
8776ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
8777{
8778 ImGuiID seed = IDStack.back();
8779 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
8780#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8781 ImGuiContext& g = *Ctx;
8782 if (g.DebugHookIdInfo == id)
8783 ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
8784#endif
8785 return id;
8786}
8787
8788ImGuiID ImGuiWindow::GetID(const void* ptr)
8789{
8790 ImGuiID seed = IDStack.back();
8791 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
8792#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8793 ImGuiContext& g = *Ctx;
8794 if (g.DebugHookIdInfo == id)
8795 ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
8796#endif
8797 return id;
8798}
8799
8800ImGuiID ImGuiWindow::GetID(int n)
8801{
8802 ImGuiID seed = IDStack.back();
8803 ImGuiID id = ImHashData(&n, sizeof(n), seed);
8804#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8805 ImGuiContext& g = *Ctx;
8806 if (g.DebugHookIdInfo == id)
8807 ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
8808#endif
8809 return id;
8810}
8811
8812// This is only used in rare/specific situations to manufacture an ID out of nowhere.
8813ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
8814{
8815 ImGuiID seed = IDStack.back();
8816 ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
8817 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
8818 return id;
8819}
8820
8821void ImGui::PushID(const char* str_id)
8822{
8823 ImGuiContext& g = *GImGui;
8824 ImGuiWindow* window = g.CurrentWindow;
8825 ImGuiID id = window->GetID(str_id);
8826 window->IDStack.push_back(id);
8827}
8828
8829void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
8830{
8831 ImGuiContext& g = *GImGui;
8832 ImGuiWindow* window = g.CurrentWindow;
8833 ImGuiID id = window->GetID(str_id_begin, str_id_end);
8834 window->IDStack.push_back(id);
8835}
8836
8837void ImGui::PushID(const void* ptr_id)
8838{
8839 ImGuiContext& g = *GImGui;
8840 ImGuiWindow* window = g.CurrentWindow;
8841 ImGuiID id = window->GetID(ptr_id);
8842 window->IDStack.push_back(id);
8843}
8844
8845void ImGui::PushID(int int_id)
8846{
8847 ImGuiContext& g = *GImGui;
8848 ImGuiWindow* window = g.CurrentWindow;
8849 ImGuiID id = window->GetID(int_id);
8850 window->IDStack.push_back(id);
8851}
8852
8853// Push a given id value ignoring the ID stack as a seed.
8854void ImGui::PushOverrideID(ImGuiID id)
8855{
8856 ImGuiContext& g = *GImGui;
8857 ImGuiWindow* window = g.CurrentWindow;
8858#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8859 if (g.DebugHookIdInfo == id)
8860 DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
8861#endif
8862 window->IDStack.push_back(id);
8863}
8864
8865// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
8866// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
8867// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
8868ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
8869{
8870 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
8871#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8872 ImGuiContext& g = *GImGui;
8873 if (g.DebugHookIdInfo == id)
8874 DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
8875#endif
8876 return id;
8877}
8878
8879ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
8880{
8881 ImGuiID id = ImHashData(&n, sizeof(n), seed);
8882#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8883 ImGuiContext& g = *GImGui;
8884 if (g.DebugHookIdInfo == id)
8885 DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
8886#endif
8887 return id;
8888}
8889
8890void ImGui::PopID()
8891{
8892 ImGuiWindow* window = GImGui->CurrentWindow;
8893 IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
8894 window->IDStack.pop_back();
8895}
8896
8897ImGuiID ImGui::GetID(const char* str_id)
8898{
8899 ImGuiWindow* window = GImGui->CurrentWindow;
8900 return window->GetID(str_id);
8901}
8902
8903ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
8904{
8905 ImGuiWindow* window = GImGui->CurrentWindow;
8906 return window->GetID(str_id_begin, str_id_end);
8907}
8908
8909ImGuiID ImGui::GetID(const void* ptr_id)
8910{
8911 ImGuiWindow* window = GImGui->CurrentWindow;
8912 return window->GetID(ptr_id);
8913}
8914
8915//-----------------------------------------------------------------------------
8916// [SECTION] INPUTS
8917//-----------------------------------------------------------------------------
8918// - GetModForModKey() [Internal]
8919// - FixupKeyChord() [Internal]
8920// - GetKeyData() [Internal]
8921// - GetKeyIndex() [Internal]
8922// - GetKeyName()
8923// - GetKeyChordName() [Internal]
8924// - CalcTypematicRepeatAmount() [Internal]
8925// - GetTypematicRepeatRate() [Internal]
8926// - GetKeyPressedAmount() [Internal]
8927// - GetKeyMagnitude2d() [Internal]
8928//-----------------------------------------------------------------------------
8929// - UpdateKeyRoutingTable() [Internal]
8930// - GetRoutingIdFromOwnerId() [Internal]
8931// - GetShortcutRoutingData() [Internal]
8932// - CalcRoutingScore() [Internal]
8933// - SetShortcutRouting() [Internal]
8934// - TestShortcutRouting() [Internal]
8935//-----------------------------------------------------------------------------
8936// - IsKeyDown()
8937// - IsKeyPressed()
8938// - IsKeyReleased()
8939//-----------------------------------------------------------------------------
8940// - IsMouseDown()
8941// - IsMouseClicked()
8942// - IsMouseReleased()
8943// - IsMouseDoubleClicked()
8944// - GetMouseClickedCount()
8945// - IsMouseHoveringRect() [Internal]
8946// - IsMouseDragPastThreshold() [Internal]
8947// - IsMouseDragging()
8948// - GetMousePos()
8949// - SetMousePos() [Internal]
8950// - GetMousePosOnOpeningCurrentPopup()
8951// - IsMousePosValid()
8952// - IsAnyMouseDown()
8953// - GetMouseDragDelta()
8954// - ResetMouseDragDelta()
8955// - GetMouseCursor()
8956// - SetMouseCursor()
8957//-----------------------------------------------------------------------------
8958// - UpdateAliasKey()
8959// - GetMergedModsFromKeys()
8960// - UpdateKeyboardInputs()
8961// - UpdateMouseInputs()
8962//-----------------------------------------------------------------------------
8963// - LockWheelingWindow [Internal]
8964// - FindBestWheelingWindow [Internal]
8965// - UpdateMouseWheel() [Internal]
8966//-----------------------------------------------------------------------------
8967// - SetNextFrameWantCaptureKeyboard()
8968// - SetNextFrameWantCaptureMouse()
8969//-----------------------------------------------------------------------------
8970// - GetInputSourceName() [Internal]
8971// - DebugPrintInputEvent() [Internal]
8972// - UpdateInputEvents() [Internal]
8973//-----------------------------------------------------------------------------
8974// - GetKeyOwner() [Internal]
8975// - TestKeyOwner() [Internal]
8976// - SetKeyOwner() [Internal]
8977// - SetItemKeyOwner() [Internal]
8978// - Shortcut() [Internal]
8979//-----------------------------------------------------------------------------
8980
8981static ImGuiKeyChord GetModForModKey(ImGuiKey key)
8982{
8983 if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
8984 return ImGuiMod_Ctrl;
8985 if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
8986 return ImGuiMod_Shift;
8987 if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
8988 return ImGuiMod_Alt;
8989 if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
8990 return ImGuiMod_Super;
8991 return ImGuiMod_None;
8992}
8993
8994ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
8995{
8996 // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
8997 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
8998 if (IsModKey(key))
8999 key_chord |= GetModForModKey(key);
9000 return key_chord;
9001}
9002
9003ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
9004{
9005 ImGuiContext& g = *ctx;
9006
9007 // Special storage location for mods
9008 if (key & ImGuiMod_Mask_)
9009 key = ConvertSingleModFlagToKey(key);
9010
9011#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
9012 IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
9013 if (IsLegacyKey(key) && g.IO.KeyMap[key] != -1)
9014 key = (ImGuiKey)g.IO.KeyMap[key]; // Remap native->imgui or imgui->native
9015#else
9016 IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
9017#endif
9018 return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];
9019}
9020
9021#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
9022// Formally moved to obsolete section in 1.90.5 in spite of documented as obsolete since 1.87
9023ImGuiKey ImGui::GetKeyIndex(ImGuiKey key)
9024{
9025 ImGuiContext& g = *GImGui;
9026 IM_ASSERT(IsNamedKey(key));
9027 const ImGuiKeyData* key_data = GetKeyData(key);
9028 return (ImGuiKey)(key_data - g.IO.KeysData);
9029}
9030#endif
9031
9032// Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
9033static const char* const GKeyNames[] =
9034{
9035 "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
9036 "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
9037 "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
9038 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
9039 "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9040 "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
9041 "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
9042 "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
9043 "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
9044 "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
9045 "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
9046 "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
9047 "AppBack", "AppForward",
9048 "GamepadStart", "GamepadBack",
9049 "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
9050 "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
9051 "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
9052 "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
9053 "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
9054 "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
9055 "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
9056};
9057IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
9058
9059const char* ImGui::GetKeyName(ImGuiKey key)
9060{
9061 if (key == ImGuiKey_None)
9062 return "None";
9063#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
9064 IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
9065#else
9066 ImGuiContext& g = *GImGui;
9067 if (IsLegacyKey(key))
9068 {
9069 if (g.IO.KeyMap[key] == -1)
9070 return "N/A";
9071 IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key]));
9072 key = (ImGuiKey)g.IO.KeyMap[key];
9073 }
9074#endif
9075 if (key & ImGuiMod_Mask_)
9076 key = ConvertSingleModFlagToKey(key);
9077 if (!IsNamedKey(key))
9078 return "Unknown";
9079
9080 return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
9081}
9082
9083// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
9084// Lifetime of return value: valid until next call to same function.
9085const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
9086{
9087 ImGuiContext& g = *GImGui;
9088
9089 const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9090 if (IsModKey(key))
9091 key_chord &= ~GetModForModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
9092 ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
9093 (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
9094 (key_chord & ImGuiMod_Shift) ? "Shift+" : "",
9095 (key_chord & ImGuiMod_Alt) ? "Alt+" : "",
9096 (key_chord & ImGuiMod_Super) ? "Super+" : "",
9097 (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
9098 size_t len;
9099 if (key == ImGuiKey_None && key_chord != 0)
9100 if ((len = strlen(g.TempKeychordName)) != 0) // Remove trailing '+'
9101 g.TempKeychordName[len - 1] = 0;
9102 return g.TempKeychordName;
9103}
9104
9105// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
9106// t1 = current time (e.g.: g.Time)
9107// An event is triggered at:
9108// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
9109int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
9110{
9111 if (t1 == 0.0f)
9112 return 1;
9113 if (t0 >= t1)
9114 return 0;
9115 if (repeat_rate <= 0.0f)
9116 return (t0 < repeat_delay) && (t1 >= repeat_delay);
9117 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
9118 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
9119 const int count = count_t1 - count_t0;
9120 return count;
9121}
9122
9123void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
9124{
9125 ImGuiContext& g = *GImGui;
9126 switch (flags & ImGuiInputFlags_RepeatRateMask_)
9127 {
9128 case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
9129 case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
9130 case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
9131 }
9132}
9133
9134// Return value representing the number of presses in the last time period, for the given repeat rate
9135// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
9136int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
9137{
9138 ImGuiContext& g = *GImGui;
9139 const ImGuiKeyData* key_data = GetKeyData(key);
9140 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)
9141 return 0;
9142 const float t = key_data->DownDuration;
9143 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
9144}
9145
9146// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
9147ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
9148{
9149 return ImVec2(
9150 GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
9151 GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
9152}
9153
9154// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
9155// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
9156// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
9157// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
9158static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
9159{
9160 ImGuiContext& g = *GImGui;
9161 rt->EntriesNext.resize(0);
9162 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9163 {
9164 const int new_routing_start_idx = rt->EntriesNext.Size;
9165 ImGuiKeyRoutingData* routing_entry;
9166 for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
9167 {
9168 routing_entry = &rt->Entries[old_routing_idx];
9169 routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
9170 routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
9171 routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
9172 routing_entry->RoutingNextScore = 255;
9173 if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
9174 continue;
9175 rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
9176
9177 // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
9178 // This is the result of previous frame's SetShortcutRouting() call.
9179 if (routing_entry->Mods == g.IO.KeyMods)
9180 {
9181 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
9182 if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
9183 {
9184 owner_data->OwnerCurr = routing_entry->RoutingCurr;
9185 //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
9186 }
9187 }
9188 }
9189
9190 // Rewrite linked-list
9191 rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
9192 for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
9193 rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
9194 }
9195 rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
9196}
9197
9198// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
9199static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
9200{
9201 ImGuiContext& g = *GImGui;
9202 return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
9203}
9204
9205ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
9206{
9207 // Majority of shortcuts will be Key + any number of Mods
9208 // We accept _Single_ mod with ImGuiKey_None.
9209 // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal
9210 // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal
9211 // - Shortcut(ImGuiMod_Ctrl); // Legal
9212 // - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal
9213 ImGuiContext& g = *GImGui;
9214 ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
9215 ImGuiKeyRoutingData* routing_data;
9216 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9217 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9218 if (key == ImGuiKey_None)
9219 key = ConvertSingleModFlagToKey(mods);
9220 IM_ASSERT(IsNamedKey(key));
9221
9222 // Get (in the majority of case, the linked list will have one element so this should be 2 reads.
9223 // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
9224 for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
9225 {
9226 routing_data = &rt->Entries[idx];
9227 if (routing_data->Mods == mods)
9228 return routing_data;
9229 }
9230
9231 // Add to linked-list
9232 ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
9233 rt->Entries.push_back(ImGuiKeyRoutingData());
9234 routing_data = &rt->Entries[routing_data_idx];
9235 routing_data->Mods = (ImU16)mods;
9236 routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
9237 rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
9238 return routing_data;
9239}
9240
9241// Current score encoding (lower is highest priority):
9242// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
9243// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
9244// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
9245// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
9246// - 254: ImGuiInputFlags_RouteGlobal
9247// - 255: never route
9248// 'flags' should include an explicit routing policy
9249static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
9250{
9251 ImGuiContext& g = *GImGui;
9252 if (flags & ImGuiInputFlags_RouteFocused)
9253 {
9254 // ActiveID gets top priority
9255 // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
9256 if (owner_id != 0 && g.ActiveId == owner_id)
9257 return 1;
9258
9259 // Score based on distance to focused window (lower is better)
9260 // Assuming both windows are submitting a routing request,
9261 // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
9262 // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
9263 // Assuming only WindowA is submitting a routing request,
9264 // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
9265 // This essentially follow the window->ParentWindowForFocusRoute chain.
9266 if (focus_scope_id == 0)
9267 return 255;
9268 for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
9269 if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
9270 return 3 + index_in_focus_path;
9271 return 255;
9272 }
9273 else if (flags & ImGuiInputFlags_RouteActive)
9274 {
9275 if (owner_id != 0 && g.ActiveId == owner_id)
9276 return 1;
9277 return 255;
9278 }
9279 else if (flags & ImGuiInputFlags_RouteGlobal)
9280 {
9281 if (flags & ImGuiInputFlags_RouteOverActive)
9282 return 0;
9283 if (flags & ImGuiInputFlags_RouteOverFocused)
9284 return 2;
9285 return 254;
9286 }
9287 IM_ASSERT(0);
9288 return 0;
9289}
9290
9291// We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
9292// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
9293static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
9294{
9295 // Mimic 'ignore_char_inputs' logic in InputText()
9296 ImGuiContext& g = *GImGui;
9297
9298 // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
9299 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9300 const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
9301 if (ignore_char_inputs)
9302 return false;
9303
9304 // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
9305 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9306 return g.KeysMayBeCharInput.TestBit(key);
9307}
9308
9309// Request a desired route for an input chord (key + mods).
9310// Return true if the route is available this frame.
9311// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
9312// (Conceptually this does a "Submit for next frame" + "Test for current frame".
9313// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
9314bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
9315{
9316 ImGuiContext& g = *GImGui;
9317 if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
9318 flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
9319 else
9320 IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
9321 IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
9322 if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
9323 IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
9324
9325 // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9326 key_chord = FixupKeyChord(key_chord);
9327
9328 // [DEBUG] Debug break requested by user
9329 if (g.DebugBreakInShortcutRouting == key_chord)
9330 IM_DEBUG_BREAK();
9331
9332 if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
9333 if (g.NavWindow == NULL)
9334 return false;
9335
9336 // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
9337 if (flags & ImGuiInputFlags_RouteAlways)
9338 {
9339 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
9340 return true;
9341 }
9342
9343 // Specific culling when there's an active item.
9344 if (g.ActiveId != 0 && g.ActiveId != owner_id)
9345 {
9346 if (flags & ImGuiInputFlags_RouteActive)
9347 return false;
9348
9349 // Cull shortcuts with no modifiers when it could generate a character.
9350 // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
9351 // but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
9352 // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
9353 // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
9354 if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
9355 {
9356 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
9357 return false;
9358 }
9359
9360 // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
9361 if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
9362 {
9363 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9364 if (key == ImGuiKey_None)
9365 key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
9366 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
9367 return false;
9368 }
9369 }
9370
9371 // Where do we evaluate route for?
9372 ImGuiID focus_scope_id = g.CurrentFocusScopeId;
9373 if (flags & ImGuiInputFlags_RouteFromRootWindow)
9374 focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
9375
9376 const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
9377 IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
9378 if (score == 255)
9379 return false;
9380
9381 // Submit routing for NEXT frame (assuming score is sufficient)
9382 // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
9383 ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
9384 //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
9385 if (score < routing_data->RoutingNextScore)
9386 {
9387 routing_data->RoutingNext = owner_id;
9388 routing_data->RoutingNextScore = (ImU8)score;
9389 }
9390
9391 // Return routing state for CURRENT frame
9392 if (routing_data->RoutingCurr == owner_id)
9393 IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
9394 return routing_data->RoutingCurr == owner_id;
9395}
9396
9397// Currently unused by core (but used by tests)
9398// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
9399bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
9400{
9401 const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
9402 key_chord = FixupKeyChord(key_chord);
9403 ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
9404 return routing_data->RoutingCurr == routing_id;
9405}
9406
9407// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
9408// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
9409bool ImGui::IsKeyDown(ImGuiKey key)
9410{
9411 return IsKeyDown(key, ImGuiKeyOwner_Any);
9412}
9413
9414bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
9415{
9416 const ImGuiKeyData* key_data = GetKeyData(key);
9417 if (!key_data->Down)
9418 return false;
9419 if (!TestKeyOwner(key, owner_id))
9420 return false;
9421 return true;
9422}
9423
9424bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
9425{
9426 return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9427}
9428
9429// Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
9430bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
9431{
9432 const ImGuiKeyData* key_data = GetKeyData(key);
9433 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)
9434 return false;
9435 const float t = key_data->DownDuration;
9436 if (t < 0.0f)
9437 return false;
9438 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
9439 if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
9440 flags |= ImGuiInputFlags_Repeat;
9441
9442 bool pressed = (t == 0.0f);
9443 if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
9444 {
9445 float repeat_delay, repeat_rate;
9446 GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
9447 pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
9448 if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
9449 {
9450 // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
9451 // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
9452 ImGuiContext& g = *GImGui;
9453 double key_pressed_time = g.Time - t + 0.00001f;
9454 if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
9455 pressed = false;
9456 if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
9457 pressed = false;
9458 if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
9459 pressed = false;
9460 }
9461 }
9462 if (!pressed)
9463 return false;
9464 if (!TestKeyOwner(key, owner_id))
9465 return false;
9466 return true;
9467}
9468
9469bool ImGui::IsKeyReleased(ImGuiKey key)
9470{
9471 return IsKeyReleased(key, ImGuiKeyOwner_Any);
9472}
9473
9474bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
9475{
9476 const ImGuiKeyData* key_data = GetKeyData(key);
9477 if (key_data->DownDurationPrev < 0.0f || key_data->Down)
9478 return false;
9479 if (!TestKeyOwner(key, owner_id))
9480 return false;
9481 return true;
9482}
9483
9484bool ImGui::IsMouseDown(ImGuiMouseButton button)
9485{
9486 ImGuiContext& g = *GImGui;
9487 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9488 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.
9489}
9490
9491bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
9492{
9493 ImGuiContext& g = *GImGui;
9494 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9495 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.
9496}
9497
9498bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
9499{
9500 return IsMouseClicked(button, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);
9501}
9502
9503bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
9504{
9505 ImGuiContext& g = *GImGui;
9506 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9507 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)
9508 return false;
9509 const float t = g.IO.MouseDownDuration[button];
9510 if (t < 0.0f)
9511 return false;
9512 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
9513
9514 const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
9515 const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
9516 if (!pressed)
9517 return false;
9518
9519 if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
9520 return false;
9521
9522 return true;
9523}
9524
9525bool ImGui::IsMouseReleased(ImGuiMouseButton button)
9526{
9527 ImGuiContext& g = *GImGui;
9528 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9529 return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
9530}
9531
9532bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
9533{
9534 ImGuiContext& g = *GImGui;
9535 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9536 return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
9537}
9538
9539bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
9540{
9541 ImGuiContext& g = *GImGui;
9542 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9543 return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
9544}
9545
9546bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
9547{
9548 ImGuiContext& g = *GImGui;
9549 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9550 return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
9551}
9552
9553int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
9554{
9555 ImGuiContext& g = *GImGui;
9556 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9557 return g.IO.MouseClickedCount[button];
9558}
9559
9560// Test if mouse cursor is hovering given rectangle
9561// NB- Rectangle is clipped by our current clip setting
9562// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
9563bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
9564{
9565 ImGuiContext& g = *GImGui;
9566
9567 // Clip
9568 ImRect rect_clipped(r_min, r_max);
9569 if (clip)
9570 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
9571
9572 // Hit testing, expanded for touch input
9573 if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
9574 return false;
9575 if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped))
9576 return false;
9577 return true;
9578}
9579
9580// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
9581// [Internal] This doesn't test if the button is pressed
9582bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
9583{
9584 ImGuiContext& g = *GImGui;
9585 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9586 if (lock_threshold < 0.0f)
9587 lock_threshold = g.IO.MouseDragThreshold;
9588 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
9589}
9590
9591bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
9592{
9593 ImGuiContext& g = *GImGui;
9594 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9595 if (!g.IO.MouseDown[button])
9596 return false;
9597 return IsMouseDragPastThreshold(button, lock_threshold);
9598}
9599
9600ImVec2 ImGui::GetMousePos()
9601{
9602 ImGuiContext& g = *GImGui;
9603 return g.IO.MousePos;
9604}
9605
9606// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
9607// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
9608void ImGui::TeleportMousePos(const ImVec2& pos)
9609{
9610 ImGuiContext& g = *GImGui;
9611 g.IO.MousePos = g.IO.MousePosPrev = pos;
9612 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
9613 g.IO.WantSetMousePos = true;
9614 //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
9615}
9616
9617// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
9618ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
9619{
9620 ImGuiContext& g = *GImGui;
9621 if (g.BeginPopupStack.Size > 0)
9622 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
9623 return g.IO.MousePos;
9624}
9625
9626// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
9627bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
9628{
9629 // The assert is only to silence a false-positive in XCode Static Analysis.
9630 // 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).
9631 IM_ASSERT(GImGui != NULL);
9632 const float MOUSE_INVALID = -256000.0f;
9633 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
9634 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
9635}
9636
9637// [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.
9638bool ImGui::IsAnyMouseDown()
9639{
9640 ImGuiContext& g = *GImGui;
9641 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
9642 if (g.IO.MouseDown[n])
9643 return true;
9644 return false;
9645}
9646
9647// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
9648// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
9649// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
9650ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
9651{
9652 ImGuiContext& g = *GImGui;
9653 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9654 if (lock_threshold < 0.0f)
9655 lock_threshold = g.IO.MouseDragThreshold;
9656 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
9657 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
9658 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
9659 return g.IO.MousePos - g.IO.MouseClickedPos[button];
9660 return ImVec2(0.0f, 0.0f);
9661}
9662
9663void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
9664{
9665 ImGuiContext& g = *GImGui;
9666 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9667 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
9668 g.IO.MouseClickedPos[button] = g.IO.MousePos;
9669}
9670
9671// Get desired mouse cursor shape.
9672// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
9673// updated during the frame, and locked in EndFrame()/Render().
9674// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
9675ImGuiMouseCursor ImGui::GetMouseCursor()
9676{
9677 ImGuiContext& g = *GImGui;
9678 return g.MouseCursor;
9679}
9680
9681void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
9682{
9683 ImGuiContext& g = *GImGui;
9684 g.MouseCursor = cursor_type;
9685}
9686
9687static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
9688{
9689 IM_ASSERT(ImGui::IsAliasKey(key));
9690 ImGuiKeyData* key_data = ImGui::GetKeyData(key);
9691 key_data->Down = v;
9692 key_data->AnalogValue = analog_value;
9693}
9694
9695// [Internal] Do not use directly
9696static ImGuiKeyChord GetMergedModsFromKeys()
9697{
9698 ImGuiKeyChord mods = 0;
9699 if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; }
9700 if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; }
9701 if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; }
9702 if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; }
9703 return mods;
9704}
9705
9706static void ImGui::UpdateKeyboardInputs()
9707{
9708 ImGuiContext& g = *GImGui;
9709 ImGuiIO& io = g.IO;
9710
9711 // Import legacy keys or verify they are not used
9712#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
9713 if (io.BackendUsingLegacyKeyArrays == 0)
9714 {
9715 // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.
9716 for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)
9717 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!");
9718 }
9719 else
9720 {
9721 if (g.FrameCount == 0)
9722 for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
9723 IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!");
9724
9725 // Build reverse KeyMap (Named -> Legacy)
9726 for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
9727 if (io.KeyMap[n] != -1)
9728 {
9729 IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));
9730 io.KeyMap[io.KeyMap[n]] = n;
9731 }
9732
9733 // Import legacy keys into new ones
9734 for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
9735 if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)
9736 {
9737 const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
9738 IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
9739 io.KeysData[key].Down = io.KeysDown[n];
9740 if (key != n)
9741 io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends
9742 io.BackendUsingLegacyKeyArrays = 1;
9743 }
9744 if (io.BackendUsingLegacyKeyArrays == 1)
9745 {
9746 GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl;
9747 GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift;
9748 GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt;
9749 GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;
9750 }
9751 }
9752#endif
9753
9754 // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys
9755#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
9756 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9757 if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)
9758 {
9759 #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)
9760 #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)
9761 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);
9762 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);
9763 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);
9764 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);
9765 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);
9766 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);
9767 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);
9768 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);
9769 MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);
9770 MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);
9771 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);
9772 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);
9773 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);
9774 MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);
9775 #undef NAV_MAP_KEY
9776 }
9777#endif
9778
9779 // Update aliases
9780 for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
9781 UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
9782 UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
9783 UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
9784
9785 // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
9786 // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
9787 // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
9788 // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
9789 const ImGuiKeyChord prev_key_mods = io.KeyMods;
9790 io.KeyMods = GetMergedModsFromKeys();
9791 io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
9792 io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
9793 io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
9794 io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
9795 if (prev_key_mods != io.KeyMods)
9796 g.LastKeyModsChangeTime = g.Time;
9797 if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
9798 g.LastKeyModsChangeFromNoneTime = g.Time;
9799
9800 // Clear gamepad data if disabled
9801 if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
9802 for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)
9803 {
9804 io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false;
9805 io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f;
9806 }
9807
9808 // Update keys
9809 for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)
9810 {
9811 ImGuiKeyData* key_data = &io.KeysData[i];
9812 key_data->DownDurationPrev = key_data->DownDuration;
9813 key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
9814 if (key_data->DownDuration == 0.0f)
9815 {
9816 ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i);
9817 if (IsKeyboardKey(key))
9818 g.LastKeyboardKeyPressTime = g.Time;
9819 else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
9820 g.LastKeyboardKeyPressTime = g.Time;
9821 }
9822 }
9823
9824 // Update keys/input owner (named keys only): one entry per key
9825 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9826 {
9827 ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];
9828 ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
9829 owner_data->OwnerCurr = owner_data->OwnerNext;
9830 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.
9831 owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
9832 owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
9833 }
9834
9835 // Update key routing (for e.g. shortcuts)
9836 UpdateKeyRoutingTable(&g.KeysRoutingTable);
9837}
9838
9839static void ImGui::UpdateMouseInputs()
9840{
9841 ImGuiContext& g = *GImGui;
9842 ImGuiIO& io = g.IO;
9843
9844 // Mouse Wheel swapping flag
9845 // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
9846 // - We avoid doing it on OSX as it the OS input layer handles this already.
9847 // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
9848 // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
9849 io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
9850
9851 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
9852 if (IsMousePosValid(&io.MousePos))
9853 io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
9854
9855 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
9856 if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
9857 io.MouseDelta = io.MousePos - io.MousePosPrev;
9858 else
9859 io.MouseDelta = ImVec2(0.0f, 0.0f);
9860
9861 // Update stationary timer.
9862 // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
9863 const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
9864 const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
9865 g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
9866 //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
9867
9868 // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
9869 if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
9870 g.NavDisableMouseHover = false;
9871
9872 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
9873 {
9874 io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
9875 io.MouseClickedCount[i] = 0; // Will be filled below
9876 io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
9877 io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
9878 io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
9879 if (io.MouseClicked[i])
9880 {
9881 bool is_repeated_click = false;
9882 if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
9883 {
9884 ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
9885 if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
9886 is_repeated_click = true;
9887 }
9888 if (is_repeated_click)
9889 io.MouseClickedLastCount[i]++;
9890 else
9891 io.MouseClickedLastCount[i] = 1;
9892 io.MouseClickedTime[i] = g.Time;
9893 io.MouseClickedPos[i] = io.MousePos;
9894 io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
9895 io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
9896 io.MouseDragMaxDistanceSqr[i] = 0.0f;
9897 }
9898 else if (io.MouseDown[i])
9899 {
9900 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
9901 ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
9902 io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
9903 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);
9904 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);
9905 }
9906
9907 // We provide io.MouseDoubleClicked[] as a legacy service
9908 io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
9909
9910 // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
9911 if (io.MouseClicked[i])
9912 g.NavDisableMouseHover = false;
9913 }
9914}
9915
9916static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
9917{
9918 ImGuiContext& g = *GImGui;
9919 if (window)
9920 g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
9921 else
9922 g.WheelingWindowReleaseTimer = 0.0f;
9923 if (g.WheelingWindow == window)
9924 return;
9925 IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
9926 g.WheelingWindow = window;
9927 g.WheelingWindowRefMousePos = g.IO.MousePos;
9928 if (window == NULL)
9929 {
9930 g.WheelingWindowStartFrame = -1;
9931 g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
9932 }
9933}
9934
9935static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
9936{
9937 // For each axis, find window in the hierarchy that may want to use scrolling
9938 ImGuiContext& g = *GImGui;
9939 ImGuiWindow* windows[2] = { NULL, NULL };
9940 for (int axis = 0; axis < 2; axis++)
9941 if (wheel[axis] != 0.0f)
9942 for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
9943 {
9944 // Bubble up into parent window if:
9945 // - a child window doesn't allow any scrolling.
9946 // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
9948 const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
9949 const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
9950 //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
9951 if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
9952 break; // select this window
9953 }
9954 if (windows[0] == NULL && windows[1] == NULL)
9955 return NULL;
9956
9957 // If there's only one window or only one axis then there's no ambiguity
9958 if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
9959 return windows[1] ? windows[1] : windows[0];
9960
9961 // If candidate are different windows we need to decide which one to prioritize
9962 // - First frame: only find a winner if one axis is zero.
9963 // - Subsequent frames: only find a winner when one is more than the other.
9964 if (g.WheelingWindowStartFrame == -1)
9965 g.WheelingWindowStartFrame = g.FrameCount;
9966 if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
9967 {
9968 g.WheelingWindowWheelRemainder = wheel;
9969 return NULL;
9970 }
9971 return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
9972}
9973
9974// Called by NewFrame()
9975void ImGui::UpdateMouseWheel()
9976{
9977 // Reset the locked window if we move the mouse or after the timer elapses.
9978 // 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)
9979 ImGuiContext& g = *GImGui;
9980 if (g.WheelingWindow != NULL)
9981 {
9982 g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
9983 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
9984 g.WheelingWindowReleaseTimer = 0.0f;
9985 if (g.WheelingWindowReleaseTimer <= 0.0f)
9986 LockWheelingWindow(NULL, 0.0f);
9987 }
9988
9989 ImVec2 wheel;
9990 wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
9991 wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
9992
9993 //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
9994 ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
9995 if (!mouse_window || mouse_window->Collapsed)
9996 return;
9997
9998 // Zoom / Scale window
9999 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
10000 if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
10001 {
10002 LockWheelingWindow(mouse_window, wheel.y);
10003 ImGuiWindow* window = mouse_window;
10004 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
10005 const float scale = new_font_scale / window->FontWindowScale;
10006 window->FontWindowScale = new_font_scale;
10007 if (window == window->RootWindow)
10008 {
10009 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
10010 SetWindowPos(window, window->Pos + offset, 0);
10011 window->Size = ImTrunc(window->Size * scale);
10012 window->SizeFull = ImTrunc(window->SizeFull * scale);
10013 }
10014 return;
10015 }
10016 if (g.IO.KeyCtrl)
10017 return;
10018
10019 // Mouse wheel scrolling
10020 // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
10021 if (g.IO.MouseWheelRequestAxisSwap)
10022 wheel = ImVec2(wheel.y, 0.0f);
10023
10024 // Maintain a rough average of moving magnitude on both axises
10025 // FIXME: should by based on wall clock time rather than frame-counter
10026 g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
10027 g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
10028
10029 // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
10030 wheel += g.WheelingWindowWheelRemainder;
10031 g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
10032 if (wheel.x == 0.0f && wheel.y == 0.0f)
10033 return;
10034
10035 // Mouse wheel scrolling: find target and apply
10036 // - don't renew lock if axis doesn't apply on the window.
10037 // - select a main axis when both axises are being moved.
10038 if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
10039 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
10040 {
10041 bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
10042 if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
10043 do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
10044 if (do_scroll[ImGuiAxis_X])
10045 {
10046 LockWheelingWindow(window, wheel.x);
10047 float max_step = window->InnerRect.GetWidth() * 0.67f;
10048 float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));
10049 SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
10050 g.WheelingWindowScrolledFrame = g.FrameCount;
10051 }
10052 if (do_scroll[ImGuiAxis_Y])
10053 {
10054 LockWheelingWindow(window, wheel.y);
10055 float max_step = window->InnerRect.GetHeight() * 0.67f;
10056 float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));
10057 SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
10058 g.WheelingWindowScrolledFrame = g.FrameCount;
10059 }
10060 }
10061}
10062
10063void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
10064{
10065 ImGuiContext& g = *GImGui;
10066 g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
10067}
10068
10069void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
10070{
10071 ImGuiContext& g = *GImGui;
10072 g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
10073}
10074
10075#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10076static const char* GetInputSourceName(ImGuiInputSource source)
10077{
10078 const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
10079 IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
10080 return input_source_names[source];
10081}
10082static const char* GetMouseSourceName(ImGuiMouseSource source)
10083{
10084 const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
10085 IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);
10086 return mouse_source_names[source];
10087}
10088static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
10089{
10090 ImGuiContext& g = *GImGui;
10091 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; }
10092 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; }
10093 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; }
10094 if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; }
10095 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; }
10096 if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
10097 if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
10098}
10099#endif
10100
10101// Process input queue
10102// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
10103// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
10104// - 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)
10105void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
10106{
10107 ImGuiContext& g = *GImGui;
10108 ImGuiIO& io = g.IO;
10109
10110 // Only trickle chars<>key when working with InputText()
10111 // FIXME: InputText() could parse event trail?
10112 // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
10113 const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
10114
10115 bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false;
10116 int mouse_button_changed = 0x00;
10117 ImBitArray<ImGuiKey_KeysData_SIZE> key_changed_mask;
10118
10119 int event_n = 0;
10120 for (; event_n < g.InputEventsQueue.Size; event_n++)
10121 {
10122 ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
10123 if (e->Type == ImGuiInputEventType_MousePos)
10124 {
10125 if (g.IO.WantSetMousePos)
10126 continue;
10127 // Trickling Rule: Stop processing queued events if we already handled a mouse button change
10128 ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
10129 if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
10130 break;
10131 io.MousePos = event_pos;
10132 io.MouseSource = e->MousePos.MouseSource;
10133 mouse_moved = true;
10134 }
10135 else if (e->Type == ImGuiInputEventType_MouseButton)
10136 {
10137 // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10138 const ImGuiMouseButton button = e->MouseButton.Button;
10139 IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
10140 if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
10141 break;
10142 if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
10143 break;
10144 io.MouseDown[button] = e->MouseButton.Down;
10145 io.MouseSource = e->MouseButton.MouseSource;
10146 mouse_button_changed |= (1 << button);
10147 }
10148 else if (e->Type == ImGuiInputEventType_MouseWheel)
10149 {
10150 // Trickling Rule: Stop processing queued events if we got multiple action on the event
10151 if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
10152 break;
10153 io.MouseWheelH += e->MouseWheel.WheelX;
10154 io.MouseWheel += e->MouseWheel.WheelY;
10155 io.MouseSource = e->MouseWheel.MouseSource;
10156 mouse_wheeled = true;
10157 }
10158 else if (e->Type == ImGuiInputEventType_MouseViewport)
10159 {
10160 io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID;
10161 }
10162 else if (e->Type == ImGuiInputEventType_Key)
10163 {
10164 // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10165 ImGuiKey key = e->Key.Key;
10166 IM_ASSERT(key != ImGuiKey_None);
10167 ImGuiKeyData* key_data = GetKeyData(key);
10168 const int key_data_index = (int)(key_data - g.IO.KeysData);
10169 if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0))
10170 break;
10171 key_data->Down = e->Key.Down;
10172 key_data->AnalogValue = e->Key.AnalogValue;
10173 key_changed = true;
10174 key_changed_mask.SetBit(key_data_index);
10175
10176 // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends
10177#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
10178 io.KeysDown[key_data_index] = key_data->Down;
10179 if (io.KeyMap[key_data_index] != -1)
10180 io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;
10181#endif
10182 }
10183 else if (e->Type == ImGuiInputEventType_Text)
10184 {
10185 // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
10186 if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
10187 break;
10188 unsigned int c = e->Text.Char;
10189 io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
10190 if (trickle_interleaved_keys_and_text)
10191 text_inputted = true;
10192 }
10193 else if (e->Type == ImGuiInputEventType_Focus)
10194 {
10195 // We intentionally overwrite this and process in NewFrame(), in order to give a chance
10196 // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
10197 const bool focus_lost = !e->AppFocused.Focused;
10198 io.AppFocusLost = focus_lost;
10199 }
10200 else
10201 {
10202 IM_ASSERT(0 && "Unknown event!");
10203 }
10204 }
10205
10206 // Record trail (for domain-specific applications wanting to access a precise trail)
10207 //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
10208 for (int n = 0; n < event_n; n++)
10209 g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
10210
10211 // [DEBUG]
10212#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10213 if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
10214 for (int n = 0; n < g.InputEventsQueue.Size; n++)
10215 DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
10216#endif
10217
10218 // Remaining events will be processed on the next frame
10219 if (event_n == g.InputEventsQueue.Size)
10220 g.InputEventsQueue.resize(0);
10221 else
10222 g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
10223
10224 // Clear buttons state when focus is lost
10225 // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
10226 // - we clear in EndFrame() and not now in order allow application/user code polling this flag
10227 // (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
10228 if (g.IO.AppFocusLost)
10229 g.IO.ClearInputKeys();
10230}
10231
10232ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
10233{
10234 if (!IsNamedKeyOrMod(key))
10235 return ImGuiKeyOwner_NoOwner;
10236
10237 ImGuiContext& g = *GImGui;
10238 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10239 ImGuiID owner_id = owner_data->OwnerCurr;
10240
10241 if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10242 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10243 return ImGuiKeyOwner_NoOwner;
10244
10245 return owner_id;
10246}
10247
10248// TestKeyOwner(..., ID) : (owner == None || owner == ID)
10249// TestKeyOwner(..., None) : (owner == None)
10250// TestKeyOwner(..., Any) : no owner test
10251// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
10252bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
10253{
10254 if (!IsNamedKeyOrMod(key))
10255 return true;
10256
10257 ImGuiContext& g = *GImGui;
10258 if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10259 if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10260 return false;
10261
10262 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10263 if (owner_id == ImGuiKeyOwner_Any)
10264 return (owner_data->LockThisFrame == false);
10265
10266 // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
10267 // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
10268 // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
10269 if (owner_data->OwnerCurr != owner_id)
10270 {
10271 if (owner_data->LockThisFrame)
10272 return false;
10273 if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
10274 return false;
10275 }
10276
10277 return true;
10278}
10279
10280// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
10281// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
10282// - SetKeyOwner(..., None) : clears owner
10283// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
10284// - SetKeyOwner(..., Any or None, Lock) : set lock
10285void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
10286{
10287 ImGuiContext& g = *GImGui;
10288 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)
10289 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
10290 //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
10291
10292 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10293 owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
10294
10295 // We cannot lock by default as it would likely break lots of legacy code.
10296 // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
10297 owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
10298 owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
10299}
10300
10301// Rarely used helper
10302void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
10303{
10304 if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
10305 if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
10306 if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
10307 if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
10308 if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
10309}
10310
10311// This is more or less equivalent to:
10312// if (IsItemHovered() || IsItemActive())
10313// SetKeyOwner(key, GetItemID());
10314// 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.
10315// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
10316// 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.
10317void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
10318{
10319 ImGuiContext& g = *GImGui;
10320 ImGuiID id = g.LastItemData.ID;
10321 if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
10322 return;
10323 if ((flags & ImGuiInputFlags_CondMask_) == 0)
10324 flags |= ImGuiInputFlags_CondDefault_;
10325 if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
10326 {
10327 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
10328 SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
10329 }
10330}
10331
10332// This is the only public API until we expose owner_id versions of the API as replacements.
10333bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
10334{
10335 return IsKeyChordPressed(key_chord, 0, ImGuiInputFlags_None);
10336}
10337
10338// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
10339bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10340{
10341 ImGuiContext& g = *GImGui;
10342 key_chord = FixupKeyChord(key_chord);
10343 ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
10344 if (g.IO.KeyMods != mods)
10345 return false;
10346
10347 // Special storage location for mods
10348 ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
10349 if (key == ImGuiKey_None)
10350 key = ConvertSingleModFlagToKey(mods);
10351 if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
10352 return false;
10353 return true;
10354}
10355
10356void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10357{
10358 ImGuiContext& g = *GImGui;
10359 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasShortcut;
10360 g.NextItemData.Shortcut = key_chord;
10361 g.NextItemData.ShortcutFlags = flags;
10362}
10363
10364void ImGui::ItemHandleShortcut(ImGuiID id)
10365{
10366 ImGuiContext& g = *GImGui;
10367 ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
10368 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
10369
10370 if (flags & ImGuiInputFlags_Tooltip)
10371 {
10372 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
10373 g.LastItemData.Shortcut = g.NextItemData.Shortcut;
10374 }
10375 if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
10376 return;
10377
10378 // FIXME: Generalize Activation queue?
10379 g.NavActivateId = id; // Will effectively disable clipping.
10380 g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
10381 //if (g.ActiveId == 0 || g.ActiveId == id)
10382 g.NavActivateDownId = g.NavActivatePressedId = id;
10383 NavHighlightActivated(id);
10384}
10385
10386bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10387{
10388 return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
10389}
10390
10391bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10392{
10393 //ImGuiContext& g = *GImGui;
10394 //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
10395
10396 // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
10397 if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
10398 flags |= ImGuiInputFlags_RouteFocused;
10399
10400 // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
10401 // Effectively makes Shortcut() always input-owner aware.
10402 if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
10403 owner_id = GetRoutingIdFromOwnerId(owner_id);
10404
10405 // Submit route
10406 if (!SetShortcutRouting(key_chord, flags, owner_id))
10407 return false;
10408
10409 // Default repeat behavior for Shortcut()
10410 // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
10411 if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
10412 flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
10413
10414 if (!IsKeyChordPressed(key_chord, flags, owner_id))
10415 return false;
10416
10417 // Claim mods during the press
10418 SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
10419
10420 IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
10421 return true;
10422}
10423
10424
10425//-----------------------------------------------------------------------------
10426// [SECTION] ERROR CHECKING
10427//-----------------------------------------------------------------------------
10428
10429// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
10430// Called by IMGUI_CHECKVERSION().
10431// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
10432// If this triggers you have mismatched headers and compiled code versions.
10433// - It could be because of a build issue (using new headers with old compiled code)
10434// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
10435// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
10436// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
10437// Otherwise it is possible that different compilation units would see different structure layout.
10438// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
10439bool 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)
10440{
10441 bool error = false;
10442 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
10443 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
10444 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
10445 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
10446 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
10447 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
10448 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
10449 return !error;
10450}
10451
10452// 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)
10453// This is causing issues and ambiguity and we need to retire that.
10454// See https://github.com/ocornut/imgui/issues/5548 for more details.
10455// [Scenario 1]
10456// Previously this would make the window content size ~200x200:
10457// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK
10458// Instead, please submit an item:
10459// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
10460// Alternative:
10461// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
10462// [Scenario 2]
10463// For reference this is one of the issue what we aim to fix with this change:
10464// BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup()
10465// 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!
10466// 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.
10467void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10468{
10469 ImGuiContext& g = *GImGui;
10470 ImGuiWindow* window = g.CurrentWindow;
10471 IM_ASSERT(window->DC.IsSetPos);
10472 window->DC.IsSetPos = false;
10473#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
10474 if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
10475 return;
10476 if (window->SkipItems)
10477 return;
10478 IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent.");
10479#else
10480 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
10481#endif
10482}
10483
10484static void ImGui::ErrorCheckNewFrameSanityChecks()
10485{
10486 ImGuiContext& g = *GImGui;
10487
10488 // Check user IM_ASSERT macro
10489 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
10490 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
10491 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
10492 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
10493 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
10494 if (true) IM_ASSERT(1); else IM_ASSERT(0);
10495
10496 // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
10497 // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
10498#ifdef __EMSCRIPTEN__
10499 if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
10500 g.IO.DeltaTime = 0.00001f;
10501#endif
10502
10503 // Check user data
10504 // (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)
10505 IM_ASSERT(g.Initialized);
10506 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
10507 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
10508 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
10509 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()");
10510 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
10511 IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
10512 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
10513 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
10514 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
10515 IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
10516#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
10517 for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)
10518 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)");
10519
10520 // 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)
10521 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)
10522 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
10523#endif
10524
10525 // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
10526 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
10527 g.IO.ConfigWindowsResizeFromEdges = false;
10528
10529 // 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.
10530 if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0)
10531 IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
10532 if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0)
10533 IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!");
10534
10535 // Perform simple checks: multi-viewport and platform windows support
10536 if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
10537 {
10538 if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports))
10539 {
10540 IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference.");
10541 IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?");
10542 IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?");
10543 IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?");
10544 IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?");
10545 IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?");
10546 IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?");
10547 IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?");
10548 IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport.");
10549 if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
10550 IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!");
10551 }
10552 else
10553 {
10554 // Disable feature, our backends do not support it
10555 g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable;
10556 }
10557
10558 // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs
10559 for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors)
10560 {
10561 IM_UNUSED(mon);
10562 IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly.");
10563 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.");
10564 IM_ASSERT(mon.DpiScale != 0.0f);
10565 }
10566 }
10567}
10568
10569static void ImGui::ErrorCheckEndFrameSanityChecks()
10570{
10571 ImGuiContext& g = *GImGui;
10572
10573 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
10574 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
10575 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
10576 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
10577 // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
10578 // while still correctly asserting on mid-frame key press events.
10579 const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
10580 IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
10581 IM_UNUSED(key_mods);
10582
10583 // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame().
10584 //ErrorCheckEndFrameRecover();
10585
10586 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
10587 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
10588 if (g.CurrentWindowStack.Size != 1)
10589 {
10590 if (g.CurrentWindowStack.Size > 1)
10591 {
10592 ImGuiWindow* window = g.CurrentWindowStack.back().Window; // <-- This window was not Ended!
10593 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
10594 IM_UNUSED(window);
10595 while (g.CurrentWindowStack.Size > 1)
10596 End();
10597 }
10598 else
10599 {
10600 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
10601 }
10602 }
10603
10604 IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
10605}
10606
10607// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
10608// Must be called during or before EndFrame().
10609// This is generally flawed as we are not necessarily End/Popping things in the right order.
10610// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
10611// FIXME: Can't recover from interleaved BeginTabBar/Begin
10612void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
10613{
10614 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
10615 ImGuiContext& g = *GImGui;
10616 while (g.CurrentWindowStack.Size > 0) //-V1044
10617 {
10618 ErrorCheckEndWindowRecover(log_callback, user_data);
10619 ImGuiWindow* window = g.CurrentWindow;
10620 if (g.CurrentWindowStack.Size == 1)
10621 {
10622 IM_ASSERT(window->IsFallbackWindow);
10623 break;
10624 }
10625 if (window->Flags & ImGuiWindowFlags_ChildWindow)
10626 {
10627 if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
10628 EndChild();
10629 }
10630 else
10631 {
10632 if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
10633 End();
10634 }
10635 }
10636}
10637
10638// Must be called before End()/EndChild()
10639void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)
10640{
10641 ImGuiContext& g = *GImGui;
10642 while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
10643 {
10644 if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
10645 EndTable();
10646 }
10647
10648 ImGuiWindow* window = g.CurrentWindow;
10649 ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;
10650 IM_ASSERT(window != NULL);
10651 while (g.CurrentTabBar != NULL) //-V1044
10652 {
10653 if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
10654 EndTabBar();
10655 }
10656 while (window->DC.TreeDepth > 0)
10657 {
10658 if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
10659 TreePop();
10660 }
10661 while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044
10662 {
10663 if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
10664 EndGroup();
10665 }
10666 while (window->IDStack.Size > 1)
10667 {
10668 if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
10669 PopID();
10670 }
10671 while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044
10672 {
10673 if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name);
10674 EndDisabled();
10675 }
10676 while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)
10677 {
10678 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
10679 PopStyleColor();
10680 }
10681 while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044
10682 {
10683 if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name);
10684 PopItemFlag();
10685 }
10686 while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044
10687 {
10688 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
10689 PopStyleVar();
10690 }
10691 while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044
10692 {
10693 if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'", window->Name);
10694 PopFont();
10695 }
10696 while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044
10697 {
10698 if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
10699 PopFocusScope();
10700 }
10701}
10702
10703// Save current stack sizes for later compare
10704void ImGuiStackSizes::SetToContextState(ImGuiContext* ctx)
10705{
10706 ImGuiContext& g = *ctx;
10707 ImGuiWindow* window = g.CurrentWindow;
10708 SizeOfIDStack = (short)window->IDStack.Size;
10709 SizeOfColorStack = (short)g.ColorStack.Size;
10710 SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
10711 SizeOfFontStack = (short)g.FontStack.Size;
10712 SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
10713 SizeOfGroupStack = (short)g.GroupStack.Size;
10714 SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
10715 SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
10716 SizeOfDisabledStack = (short)g.DisabledStackSize;
10717}
10718
10719// Compare to detect usage errors
10720void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)
10721{
10722 ImGuiContext& g = *ctx;
10723 ImGuiWindow* window = g.CurrentWindow;
10724 IM_UNUSED(window);
10725
10726 // Window stacks
10727 // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
10728 IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!");
10729
10730 // Global stacks
10731 // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
10732 IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
10733 IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
10734 IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!");
10735 IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!");
10736 IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
10737 IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
10738 IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
10739 IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!");
10740}
10741
10742//-----------------------------------------------------------------------------
10743// [SECTION] ITEM SUBMISSION
10744//-----------------------------------------------------------------------------
10745// - KeepAliveID()
10746// - ItemAdd()
10747//-----------------------------------------------------------------------------
10748
10749// 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().
10750void ImGui::KeepAliveID(ImGuiID id)
10751{
10752 ImGuiContext& g = *GImGui;
10753 if (g.ActiveId == id)
10754 g.ActiveIdIsAlive = id;
10755 if (g.ActiveIdPreviousFrame == id)
10756 g.ActiveIdPreviousFrameIsAlive = true;
10757}
10758
10759// Declare item bounding box for clipping and interaction.
10760// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
10761// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
10762// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
10763bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
10764{
10765 ImGuiContext& g = *GImGui;
10766 ImGuiWindow* window = g.CurrentWindow;
10767
10768 // Set item data
10769 // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
10770 g.LastItemData.ID = id;
10771 g.LastItemData.Rect = bb;
10772 g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
10773 g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
10774 g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
10775 // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
10776
10777 if (id != 0)
10778 {
10779 KeepAliveID(id);
10780
10781 // Directional navigation processing
10782 // Runs prior to clipping early-out
10783 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
10784 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
10785 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
10786 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
10787 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
10788 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
10789 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
10790 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
10791 if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav))
10792 {
10793 // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
10794 window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
10795 if (g.NavId == id || g.NavAnyRequest)
10796 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
10797 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
10798 NavProcessItem();
10799 }
10800
10801 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasShortcut)
10802 ItemHandleShortcut(id);
10803 }
10804
10805 // Lightweight clear of SetNextItemXXX data.
10806 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
10807 g.NextItemData.ItemFlags = ImGuiItemFlags_None;
10808
10809#ifdef IMGUI_ENABLE_TEST_ENGINE
10810 if (id != 0)
10811 IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
10812#endif
10813
10814 // Clipping test
10815 // (this is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value)
10816 //const bool is_clipped = IsClippedEx(bb, id);
10817 //if (is_clipped)
10818 // return false;
10819 // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
10820 const bool is_rect_visible = bb.Overlaps(window->ClipRect);
10821 if (!is_rect_visible)
10822 if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
10823 if (!g.LogEnabled)
10824 return false;
10825
10826 // [DEBUG]
10827#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10828 if (id != 0)
10829 {
10830 if (id == g.DebugLocateId)
10831 DebugLocateItemResolveWithLastItem();
10832
10833 // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
10834 // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
10835 // READ THE FAQ: https://dearimgui.com/faq
10836 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!");
10837 }
10838 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
10839 //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0)
10840 // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
10841#endif
10842
10843 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
10844 if (is_rect_visible)
10845 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
10846 if (IsMouseHoveringRect(bb.Min, bb.Max))
10847 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
10848 return true;
10849}
10850
10851
10852//-----------------------------------------------------------------------------
10853// [SECTION] LAYOUT
10854//-----------------------------------------------------------------------------
10855// - ItemSize()
10856// - SameLine()
10857// - GetCursorScreenPos()
10858// - SetCursorScreenPos()
10859// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
10860// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
10861// - GetCursorStartPos()
10862// - Indent()
10863// - Unindent()
10864// - SetNextItemWidth()
10865// - PushItemWidth()
10866// - PushMultiItemsWidths()
10867// - PopItemWidth()
10868// - CalcItemWidth()
10869// - CalcItemSize()
10870// - GetTextLineHeight()
10871// - GetTextLineHeightWithSpacing()
10872// - GetFrameHeight()
10873// - GetFrameHeightWithSpacing()
10874// - GetContentRegionMax()
10875// - GetContentRegionMaxAbs() [Internal]
10876// - GetContentRegionAvail(),
10877// - GetWindowContentRegionMin(), GetWindowContentRegionMax()
10878// - BeginGroup()
10879// - EndGroup()
10880// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
10881//-----------------------------------------------------------------------------
10882
10883// Advance cursor given item size for layout.
10884// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
10885// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
10886// THIS IS IN THE PERFORMANCE CRITICAL PATH.
10887void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
10888{
10889 ImGuiContext& g = *GImGui;
10890 ImGuiWindow* window = g.CurrentWindow;
10891 if (window->SkipItems)
10892 return;
10893
10894 // We increase the height in this function to accommodate for baseline offset.
10895 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
10896 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
10897 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
10898
10899 const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
10900 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);
10901
10902 // Always align ourselves on pixel boundaries
10903 //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]
10904 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
10905 window->DC.CursorPosPrevLine.y = line_y1;
10906 window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
10907 window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
10908 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
10909 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
10910 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
10911
10912 window->DC.PrevLineSize.y = line_height;
10913 window->DC.CurrLineSize.y = 0.0f;
10914 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
10915 window->DC.CurrLineTextBaseOffset = 0.0f;
10916 window->DC.IsSameLine = window->DC.IsSetPos = false;
10917
10918 // Horizontal layout mode
10919 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
10920 SameLine();
10921}
10922
10923// Gets back to previous line and continue with horizontal layout
10924// offset_from_start_x == 0 : follow right after previous item
10925// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
10926// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
10927// spacing_w >= 0 : enforce spacing amount
10928void ImGui::SameLine(float offset_from_start_x, float spacing_w)
10929{
10930 ImGuiContext& g = *GImGui;
10931 ImGuiWindow* window = g.CurrentWindow;
10932 if (window->SkipItems)
10933 return;
10934
10935 if (offset_from_start_x != 0.0f)
10936 {
10937 if (spacing_w < 0.0f)
10938 spacing_w = 0.0f;
10939 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
10940 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
10941 }
10942 else
10943 {
10944 if (spacing_w < 0.0f)
10945 spacing_w = g.Style.ItemSpacing.x;
10946 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
10947 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
10948 }
10949 window->DC.CurrLineSize = window->DC.PrevLineSize;
10950 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
10951 window->DC.IsSameLine = true;
10952}
10953
10954ImVec2 ImGui::GetCursorScreenPos()
10955{
10956 ImGuiWindow* window = GetCurrentWindowRead();
10957 return window->DC.CursorPos;
10958}
10959
10960void ImGui::SetCursorScreenPos(const ImVec2& pos)
10961{
10962 ImGuiWindow* window = GetCurrentWindow();
10963 window->DC.CursorPos = pos;
10964 //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
10965 window->DC.IsSetPos = true;
10966}
10967
10968// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
10969// 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'.
10970ImVec2 ImGui::GetCursorPos()
10971{
10972 ImGuiWindow* window = GetCurrentWindowRead();
10973 return window->DC.CursorPos - window->Pos + window->Scroll;
10974}
10975
10976float ImGui::GetCursorPosX()
10977{
10978 ImGuiWindow* window = GetCurrentWindowRead();
10979 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
10980}
10981
10982float ImGui::GetCursorPosY()
10983{
10984 ImGuiWindow* window = GetCurrentWindowRead();
10985 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
10986}
10987
10988void ImGui::SetCursorPos(const ImVec2& local_pos)
10989{
10990 ImGuiWindow* window = GetCurrentWindow();
10991 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
10992 //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
10993 window->DC.IsSetPos = true;
10994}
10995
10996void ImGui::SetCursorPosX(float x)
10997{
10998 ImGuiWindow* window = GetCurrentWindow();
10999 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
11000 //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
11001 window->DC.IsSetPos = true;
11002}
11003
11004void ImGui::SetCursorPosY(float y)
11005{
11006 ImGuiWindow* window = GetCurrentWindow();
11007 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
11008 //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
11009 window->DC.IsSetPos = true;
11010}
11011
11012ImVec2 ImGui::GetCursorStartPos()
11013{
11014 ImGuiWindow* window = GetCurrentWindowRead();
11015 return window->DC.CursorStartPos - window->Pos;
11016}
11017
11018void ImGui::Indent(float indent_w)
11019{
11020 ImGuiContext& g = *GImGui;
11021 ImGuiWindow* window = GetCurrentWindow();
11022 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11023 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11024}
11025
11026void ImGui::Unindent(float indent_w)
11027{
11028 ImGuiContext& g = *GImGui;
11029 ImGuiWindow* window = GetCurrentWindow();
11030 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11031 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11032}
11033
11034// Affect large frame+labels widgets only.
11035void ImGui::SetNextItemWidth(float item_width)
11036{
11037 ImGuiContext& g = *GImGui;
11038 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
11039 g.NextItemData.Width = item_width;
11040}
11041
11042// FIXME: Remove the == 0.0f behavior?
11043void ImGui::PushItemWidth(float item_width)
11044{
11045 ImGuiContext& g = *GImGui;
11046 ImGuiWindow* window = g.CurrentWindow;
11047 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11048 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
11049 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
11050}
11051
11052void ImGui::PushMultiItemsWidths(int components, float w_full)
11053{
11054 ImGuiContext& g = *GImGui;
11055 ImGuiWindow* window = g.CurrentWindow;
11056 IM_ASSERT(components > 0);
11057 const ImGuiStyle& style = g.Style;
11058 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11059 float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
11060 float prev_split = w_items;
11061 for (int i = components - 1; i > 0; i--)
11062 {
11063 float next_split = IM_TRUNC(w_items * i / components);
11064 window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
11065 prev_split = next_split;
11066 }
11067 window->DC.ItemWidth = ImMax(prev_split, 1.0f);
11068 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
11069}
11070
11071void ImGui::PopItemWidth()
11072{
11073 ImGuiWindow* window = GetCurrentWindow();
11074 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
11075 window->DC.ItemWidthStack.pop_back();
11076}
11077
11078// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
11079// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
11080float ImGui::CalcItemWidth()
11081{
11082 ImGuiContext& g = *GImGui;
11083 ImGuiWindow* window = g.CurrentWindow;
11084 float w;
11085 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
11086 w = g.NextItemData.Width;
11087 else
11088 w = window->DC.ItemWidth;
11089 if (w < 0.0f)
11090 {
11091 float region_max_x = GetContentRegionMaxAbs().x;
11092 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
11093 }
11094 w = IM_TRUNC(w);
11095 return w;
11096}
11097
11098// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
11099// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
11100// Note that only CalcItemWidth() is publicly exposed.
11101// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
11102ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
11103{
11104 ImGuiContext& g = *GImGui;
11105 ImGuiWindow* window = g.CurrentWindow;
11106
11107 ImVec2 region_max;
11108 if (size.x < 0.0f || size.y < 0.0f)
11109 region_max = GetContentRegionMaxAbs();
11110
11111 if (size.x == 0.0f)
11112 size.x = default_w;
11113 else if (size.x < 0.0f)
11114 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
11115
11116 if (size.y == 0.0f)
11117 size.y = default_h;
11118 else if (size.y < 0.0f)
11119 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
11120
11121 return size;
11122}
11123
11124float ImGui::GetTextLineHeight()
11125{
11126 ImGuiContext& g = *GImGui;
11127 return g.FontSize;
11128}
11129
11130float ImGui::GetTextLineHeightWithSpacing()
11131{
11132 ImGuiContext& g = *GImGui;
11133 return g.FontSize + g.Style.ItemSpacing.y;
11134}
11135
11136float ImGui::GetFrameHeight()
11137{
11138 ImGuiContext& g = *GImGui;
11139 return g.FontSize + g.Style.FramePadding.y * 2.0f;
11140}
11141
11142float ImGui::GetFrameHeightWithSpacing()
11143{
11144 ImGuiContext& g = *GImGui;
11145 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
11146}
11147
11148// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience!
11149
11150// FIXME: This is in window space (not screen space!).
11151ImVec2 ImGui::GetContentRegionMax()
11152{
11153 ImGuiContext& g = *GImGui;
11154 ImGuiWindow* window = g.CurrentWindow;
11155 ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11156 return mx - window->Pos;
11157}
11158
11159// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
11160ImVec2 ImGui::GetContentRegionMaxAbs()
11161{
11162 ImGuiContext& g = *GImGui;
11163 ImGuiWindow* window = g.CurrentWindow;
11164 ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11165 return mx;
11166}
11167
11168ImVec2 ImGui::GetContentRegionAvail()
11169{
11170 ImGuiWindow* window = GImGui->CurrentWindow;
11171 return GetContentRegionMaxAbs() - window->DC.CursorPos;
11172}
11173
11174// In window space (not screen space!)
11175ImVec2 ImGui::GetWindowContentRegionMin()
11176{
11177 ImGuiWindow* window = GImGui->CurrentWindow;
11178 return window->ContentRegionRect.Min - window->Pos;
11179}
11180
11181ImVec2 ImGui::GetWindowContentRegionMax()
11182{
11183 ImGuiWindow* window = GImGui->CurrentWindow;
11184 return window->ContentRegionRect.Max - window->Pos;
11185}
11186
11187// 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.)
11188// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
11189// FIXME-OPT: Could we safely early out on ->SkipItems?
11190void ImGui::BeginGroup()
11191{
11192 ImGuiContext& g = *GImGui;
11193 ImGuiWindow* window = g.CurrentWindow;
11194
11195 g.GroupStack.resize(g.GroupStack.Size + 1);
11196 ImGuiGroupData& group_data = g.GroupStack.back();
11197 group_data.WindowID = window->ID;
11198 group_data.BackupCursorPos = window->DC.CursorPos;
11199 group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
11200 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
11201 group_data.BackupIndent = window->DC.Indent;
11202 group_data.BackupGroupOffset = window->DC.GroupOffset;
11203 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
11204 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
11205 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
11206 group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
11207 group_data.BackupIsSameLine = window->DC.IsSameLine;
11208 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
11209 group_data.EmitItem = true;
11210
11211 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
11212 window->DC.Indent = window->DC.GroupOffset;
11213 window->DC.CursorMaxPos = window->DC.CursorPos;
11214 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
11215 if (g.LogEnabled)
11216 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11217}
11218
11219void ImGui::EndGroup()
11220{
11221 ImGuiContext& g = *GImGui;
11222 ImGuiWindow* window = g.CurrentWindow;
11223 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
11224
11225 ImGuiGroupData& group_data = g.GroupStack.back();
11226 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
11227
11228 if (window->DC.IsSetPos)
11229 ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
11230
11231 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
11232
11233 window->DC.CursorPos = group_data.BackupCursorPos;
11234 window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
11235 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
11236 window->DC.Indent = group_data.BackupIndent;
11237 window->DC.GroupOffset = group_data.BackupGroupOffset;
11238 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
11239 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
11240 window->DC.IsSameLine = group_data.BackupIsSameLine;
11241 if (g.LogEnabled)
11242 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11243
11244 if (!group_data.EmitItem)
11245 {
11246 g.GroupStack.pop_back();
11247 return;
11248 }
11249
11250 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.
11251 ItemSize(group_bb.GetSize());
11252 ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
11253
11254 // 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.
11255 // 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.
11256 // Also if you grep for LastItemId you'll notice it is only used in that context.
11257 // (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.)
11258 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
11259 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
11260 if (group_contains_curr_active_id)
11261 g.LastItemData.ID = g.ActiveId;
11262 else if (group_contains_prev_active_id)
11263 g.LastItemData.ID = g.ActiveIdPreviousFrame;
11264 g.LastItemData.Rect = group_bb;
11265
11266 // Forward Hovered flag
11267 const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
11268 if (group_contains_curr_hovered_id)
11269 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
11270
11271 // Forward Edited flag
11272 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
11273 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
11274
11275 // Forward Deactivated flag
11276 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
11277 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
11278 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
11279
11280 g.GroupStack.pop_back();
11281 if (g.DebugShowGroupRects)
11282 window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
11283}
11284
11285
11286//-----------------------------------------------------------------------------
11287// [SECTION] SCROLLING
11288//-----------------------------------------------------------------------------
11289
11290// Helper to snap on edges when aiming at an item very close to the edge,
11291// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
11292// When we refactor the scrolling API this may be configurable with a flag?
11293// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
11294static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
11295{
11296 if (target <= snap_min + snap_threshold)
11297 return ImLerp(snap_min, target, center_ratio);
11298 if (target >= snap_max - snap_threshold)
11299 return ImLerp(target, snap_max, center_ratio);
11300 return target;
11301}
11302
11303static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
11304{
11305 ImVec2 scroll = window->Scroll;
11306 ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
11307 for (int axis = 0; axis < 2; axis++)
11308 {
11309 if (window->ScrollTarget[axis] < FLT_MAX)
11310 {
11311 float center_ratio = window->ScrollTargetCenterRatio[axis];
11312 float scroll_target = window->ScrollTarget[axis];
11313 if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
11314 {
11315 float snap_min = 0.0f;
11316 float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
11317 scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
11318 }
11319 scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
11320 }
11321 scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
11322 if (!window->Collapsed && !window->SkipItems)
11323 scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
11324 }
11325 return scroll;
11326}
11327
11328void ImGui::ScrollToItem(ImGuiScrollFlags flags)
11329{
11330 ImGuiContext& g = *GImGui;
11331 ImGuiWindow* window = g.CurrentWindow;
11332 ScrollToRectEx(window, g.LastItemData.NavRect, flags);
11333}
11334
11335void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11336{
11337 ScrollToRectEx(window, item_rect, flags);
11338}
11339
11340// Scroll to keep newly navigated item fully into view
11341ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11342{
11343 ImGuiContext& g = *GImGui;
11344 ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
11345 scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
11346 scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
11347 //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
11348 //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
11349
11350 // Check that only one behavior is selected per axis
11351 IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
11352 IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
11353
11354 // Defaults
11355 ImGuiScrollFlags in_flags = flags;
11356 if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
11357 flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
11358 if ((flags & ImGuiScrollFlags_MaskY_) == 0)
11359 flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
11360
11361 const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
11362 const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
11363 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;
11364 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;
11365
11366 if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
11367 {
11368 if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
11369 SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
11370 else if (item_rect.Max.x >= scroll_rect.Max.x)
11371 SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
11372 }
11373 else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
11374 {
11375 if (can_be_fully_visible_x)
11376 SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
11377 else
11378 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
11379 }
11380
11381 if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
11382 {
11383 if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
11384 SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
11385 else if (item_rect.Max.y >= scroll_rect.Max.y)
11386 SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
11387 }
11388 else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
11389 {
11390 if (can_be_fully_visible_y)
11391 SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
11392 else
11393 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
11394 }
11395
11396 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
11397 ImVec2 delta_scroll = next_scroll - window->Scroll;
11398
11399 // Also scroll parent window to keep us into view if necessary
11400 if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
11401 {
11402 // FIXME-SCROLL: May be an option?
11403 if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
11404 in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
11405 if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
11406 in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
11407 delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
11408 }
11409
11410 return delta_scroll;
11411}
11412
11413float ImGui::GetScrollX()
11414{
11415 ImGuiWindow* window = GImGui->CurrentWindow;
11416 return window->Scroll.x;
11417}
11418
11419float ImGui::GetScrollY()
11420{
11421 ImGuiWindow* window = GImGui->CurrentWindow;
11422 return window->Scroll.y;
11423}
11424
11425float ImGui::GetScrollMaxX()
11426{
11427 ImGuiWindow* window = GImGui->CurrentWindow;
11428 return window->ScrollMax.x;
11429}
11430
11431float ImGui::GetScrollMaxY()
11432{
11433 ImGuiWindow* window = GImGui->CurrentWindow;
11434 return window->ScrollMax.y;
11435}
11436
11437void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
11438{
11439 window->ScrollTarget.x = scroll_x;
11440 window->ScrollTargetCenterRatio.x = 0.0f;
11441 window->ScrollTargetEdgeSnapDist.x = 0.0f;
11442}
11443
11444void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
11445{
11446 window->ScrollTarget.y = scroll_y;
11447 window->ScrollTargetCenterRatio.y = 0.0f;
11448 window->ScrollTargetEdgeSnapDist.y = 0.0f;
11449}
11450
11451void ImGui::SetScrollX(float scroll_x)
11452{
11453 ImGuiContext& g = *GImGui;
11454 SetScrollX(g.CurrentWindow, scroll_x);
11455}
11456
11457void ImGui::SetScrollY(float scroll_y)
11458{
11459 ImGuiContext& g = *GImGui;
11460 SetScrollY(g.CurrentWindow, scroll_y);
11461}
11462
11463// Note that a local position will vary depending on initial scroll value,
11464// This is a little bit confusing so bear with us:
11465// - local_pos = (absolution_pos - window->Pos)
11466// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
11467// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
11468// - They mostly exist because of legacy API.
11469// Following the rules above, when trying to work with scrolling code, consider that:
11470// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
11471// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
11472// 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
11473void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
11474{
11475 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
11476 window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
11477 window->ScrollTargetCenterRatio.x = center_x_ratio;
11478 window->ScrollTargetEdgeSnapDist.x = 0.0f;
11479}
11480
11481void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
11482{
11483 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
11484 window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
11485 window->ScrollTargetCenterRatio.y = center_y_ratio;
11486 window->ScrollTargetEdgeSnapDist.y = 0.0f;
11487}
11488
11489void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
11490{
11491 ImGuiContext& g = *GImGui;
11492 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
11493}
11494
11495void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
11496{
11497 ImGuiContext& g = *GImGui;
11498 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
11499}
11500
11501// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
11502void ImGui::SetScrollHereX(float center_x_ratio)
11503{
11504 ImGuiContext& g = *GImGui;
11505 ImGuiWindow* window = g.CurrentWindow;
11506 float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
11507 float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
11508 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
11509
11510 // Tweak: snap on edges when aiming at an item very close to the edge
11511 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
11512}
11513
11514// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
11515void ImGui::SetScrollHereY(float center_y_ratio)
11516{
11517 ImGuiContext& g = *GImGui;
11518 ImGuiWindow* window = g.CurrentWindow;
11519 float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
11520 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
11521 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
11522
11523 // Tweak: snap on edges when aiming at an item very close to the edge
11524 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
11525}
11526
11527//-----------------------------------------------------------------------------
11528// [SECTION] TOOLTIPS
11529//-----------------------------------------------------------------------------
11530
11531bool ImGui::BeginTooltip()
11532{
11533 return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
11534}
11535
11536bool ImGui::BeginItemTooltip()
11537{
11538 if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11539 return false;
11540 return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
11541}
11542
11543bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
11544{
11545 ImGuiContext& g = *GImGui;
11546
11547 if (g.DragDropWithinSource || g.DragDropWithinTarget)
11548 {
11549 // Drag and Drop tooltips are positioning differently than other tooltips:
11550 // - offset visibility to increase visibility around mouse.
11551 // - never clamp within outer viewport boundary.
11552 // We call SetNextWindowPos() to enforce position and disable clamping.
11553 // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
11554 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
11555 ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale;
11556 SetNextWindowPos(tooltip_pos);
11557 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
11558 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
11559 tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
11560 }
11561
11562 char window_name[16];
11563 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
11564 if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious)
11565 if (ImGuiWindow* window = FindWindowByName(window_name))
11566 if (window->Active)
11567 {
11568 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
11569 SetWindowHiddenAndSkipItemsForCurrentFrame(window);
11570 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
11571 }
11572 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking;
11573 Begin(window_name, NULL, flags | extra_window_flags);
11574 // 2023-03-09: Added bool return value to the API, but currently always returning true.
11575 // If this ever returns false we need to update BeginDragDropSource() accordingly.
11576 //if (!ret)
11577 // End();
11578 //return ret;
11579 return true;
11580}
11581
11582void ImGui::EndTooltip()
11583{
11584 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
11585 End();
11586}
11587
11588void ImGui::SetTooltip(const char* fmt, ...)
11589{
11590 va_list args;
11591 va_start(args, fmt);
11592 SetTooltipV(fmt, args);
11593 va_end(args);
11594}
11595
11596void ImGui::SetTooltipV(const char* fmt, va_list args)
11597{
11598 if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
11599 return;
11600 TextV(fmt, args);
11601 EndTooltip();
11602}
11603
11604// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
11605// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
11606void ImGui::SetItemTooltip(const char* fmt, ...)
11607{
11608 va_list args;
11609 va_start(args, fmt);
11610 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11611 SetTooltipV(fmt, args);
11612 va_end(args);
11613}
11614
11615void ImGui::SetItemTooltipV(const char* fmt, va_list args)
11616{
11617 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11618 SetTooltipV(fmt, args);
11619}
11620
11621
11622//-----------------------------------------------------------------------------
11623// [SECTION] POPUPS
11624//-----------------------------------------------------------------------------
11625
11626// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
11627bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
11628{
11629 ImGuiContext& g = *GImGui;
11630 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
11631 {
11632 // Return true if any popup is open at the current BeginPopup() level of the popup stack
11633 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
11634 IM_ASSERT(id == 0);
11635 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
11636 return g.OpenPopupStack.Size > 0;
11637 else
11638 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
11639 }
11640 else
11641 {
11642 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
11643 {
11644 // Return true if the popup is open anywhere in the popup stack
11645 for (ImGuiPopupData& popup_data : g.OpenPopupStack)
11646 if (popup_data.PopupId == id)
11647 return true;
11648 return false;
11649 }
11650 else
11651 {
11652 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
11653 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
11654 }
11655 }
11656}
11657
11658bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
11659{
11660 ImGuiContext& g = *GImGui;
11661 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
11662 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
11663 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
11664 return IsPopupOpen(id, popup_flags);
11665}
11666
11667// Also see FindBlockingModal(NULL)
11668ImGuiWindow* ImGui::GetTopMostPopupModal()
11669{
11670 ImGuiContext& g = *GImGui;
11671 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
11672 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
11673 if (popup->Flags & ImGuiWindowFlags_Modal)
11674 return popup;
11675 return NULL;
11676}
11677
11678// See Demo->Stacked Modal to confirm what this is for.
11679ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
11680{
11681 ImGuiContext& g = *GImGui;
11682 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
11683 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
11684 if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
11685 return popup;
11686 return NULL;
11687}
11688
11689void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
11690{
11691 ImGuiContext& g = *GImGui;
11692 ImGuiID id = g.CurrentWindow->GetID(str_id);
11693 IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
11694 OpenPopupEx(id, popup_flags);
11695}
11696
11697void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
11698{
11699 OpenPopupEx(id, popup_flags);
11700}
11701
11702// Mark popup as open (toggle toward open state).
11703// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
11704// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
11705// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
11706void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
11707{
11708 ImGuiContext& g = *GImGui;
11709 ImGuiWindow* parent_window = g.CurrentWindow;
11710 const int current_stack_size = g.BeginPopupStack.Size;
11711
11712 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
11713 if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
11714 return;
11715
11716 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
11717 popup_ref.PopupId = id;
11718 popup_ref.Window = NULL;
11719 popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
11720 popup_ref.OpenFrameCount = g.FrameCount;
11721 popup_ref.OpenParentId = parent_window->IDStack.back();
11722 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
11723 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
11724
11725 IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
11726 if (g.OpenPopupStack.Size < current_stack_size + 1)
11727 {
11728 g.OpenPopupStack.push_back(popup_ref);
11729 }
11730 else
11731 {
11732 // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
11733 // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
11734 // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
11735 // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
11736 bool keep_existing = false;
11737 if (g.OpenPopupStack[current_stack_size].PopupId == id)
11738 if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
11739 keep_existing = true;
11740 if (keep_existing)
11741 {
11742 // No reopen
11743 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
11744 }
11745 else
11746 {
11747 // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
11748 ClosePopupToLevel(current_stack_size, true);
11749 g.OpenPopupStack.push_back(popup_ref);
11750 }
11751
11752 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
11753 // This is equivalent to what ClosePopupToLevel() does.
11754 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
11755 // FocusWindow(parent_window);
11756 }
11757}
11758
11759// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
11760// This function closes any popups that are over 'ref_window'.
11761void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
11762{
11763 ImGuiContext& g = *GImGui;
11764 if (g.OpenPopupStack.Size == 0)
11765 return;
11766
11767 // Don't close our own child popup windows.
11768 //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
11769 int popup_count_to_keep = 0;
11770 if (ref_window)
11771 {
11772 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
11773 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
11774 {
11775 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
11776 if (!popup.Window)
11777 continue;
11778 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
11779
11780 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
11781 // - Clicking/Focusing Window2 won't close Popup1:
11782 // Window -> Popup1 -> Window2(Ref)
11783 // - Clicking/focusing Popup1 will close Popup2 and Popup3:
11784 // Window -> Popup1(Ref) -> Popup2 -> Popup3
11785 // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree!
11786 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
11787 // We step through every popup from bottom to top to validate their position relative to reference window.
11788 bool ref_window_is_descendent_of_popup = false;
11789 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
11790 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
11791 //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE
11792 if (IsWindowWithinBeginStackOf(ref_window, popup_window))
11793 {
11794 ref_window_is_descendent_of_popup = true;
11795 break;
11796 }
11797 if (!ref_window_is_descendent_of_popup)
11798 break;
11799 }
11800 }
11801 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
11802 {
11803 IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
11804 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
11805 }
11806}
11807
11808void ImGui::ClosePopupsExceptModals()
11809{
11810 ImGuiContext& g = *GImGui;
11811
11812 int popup_count_to_keep;
11813 for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
11814 {
11815 ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
11816 if (!window || (window->Flags & ImGuiWindowFlags_Modal))
11817 break;
11818 }
11819 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
11820 ClosePopupToLevel(popup_count_to_keep, true);
11821}
11822
11823void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
11824{
11825 ImGuiContext& g = *GImGui;
11826 IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
11827 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
11828
11829 // Trim open popup stack
11830 ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
11831 g.OpenPopupStack.resize(remaining);
11832
11833 // 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)
11834 if (restore_focus_to_window_under_popup && prev_popup.Window)
11835 {
11836 ImGuiWindow* popup_window = prev_popup.Window;
11837 ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
11838 if (focus_window && !focus_window->WasActive)
11839 FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
11840 else
11841 FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
11842 }
11843}
11844
11845// Close the popup we have begin-ed into.
11846void ImGui::CloseCurrentPopup()
11847{
11848 ImGuiContext& g = *GImGui;
11849 int popup_idx = g.BeginPopupStack.Size - 1;
11850 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
11851 return;
11852
11853 // Closing a menu closes its top-most parent popup (unless a modal)
11854 while (popup_idx > 0)
11855 {
11856 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
11857 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
11858 bool close_parent = false;
11859 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
11860 if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
11861 close_parent = true;
11862 if (!close_parent)
11863 break;
11864 popup_idx--;
11865 }
11866 IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
11867 ClosePopupToLevel(popup_idx, true);
11868
11869 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
11870 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
11871 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
11872 if (ImGuiWindow* window = g.NavWindow)
11873 window->DC.NavHideHighlightOneFrame = true;
11874}
11875
11876// Attention! BeginPopup() adds default flags which BeginPopupEx()!
11877bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
11878{
11879 ImGuiContext& g = *GImGui;
11880 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
11881 {
11882 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
11883 return false;
11884 }
11885
11886 char name[20];
11887 if (flags & ImGuiWindowFlags_ChildMenu)
11888 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth
11889 else
11890 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
11891
11892 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking;
11893 bool is_open = Begin(name, NULL, flags);
11894 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
11895 EndPopup();
11896
11897 //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
11898
11899 return is_open;
11900}
11901
11902bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
11903{
11904 ImGuiContext& g = *GImGui;
11905 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
11906 {
11907 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
11908 return false;
11909 }
11910 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
11911 ImGuiID id = g.CurrentWindow->GetID(str_id);
11912 return BeginPopupEx(id, flags);
11913}
11914
11915// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
11916// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
11917// - *p_open set back to false in BeginPopupModal() when popup is not open.
11918// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
11919bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
11920{
11921 ImGuiContext& g = *GImGui;
11922 ImGuiWindow* window = g.CurrentWindow;
11923 const ImGuiID id = window->GetID(name);
11924 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
11925 {
11926 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
11927 if (p_open && *p_open)
11928 *p_open = false;
11929 return false;
11930 }
11931
11932 // Center modal windows by default for increased visibility
11933 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
11934 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
11935 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
11936 {
11937 const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport?
11938 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
11939 }
11940
11941 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking;
11942 const bool is_open = Begin(name, p_open, flags);
11943 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
11944 {
11945 EndPopup();
11946 if (is_open)
11947 ClosePopupToLevel(g.BeginPopupStack.Size, true);
11948 return false;
11949 }
11950 return is_open;
11951}
11952
11953void ImGui::EndPopup()
11954{
11955 ImGuiContext& g = *GImGui;
11956 ImGuiWindow* window = g.CurrentWindow;
11957 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
11958 IM_ASSERT(g.BeginPopupStack.Size > 0);
11959
11960 // 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)
11961 if (g.NavWindow == window)
11962 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
11963
11964 // Child-popups don't need to be laid out
11965 IM_ASSERT(g.WithinEndChild == false);
11966 if (window->Flags & ImGuiWindowFlags_ChildWindow)
11967 g.WithinEndChild = true;
11968 End();
11969 g.WithinEndChild = false;
11970}
11971
11972// Helper to open a popup if mouse button is released over the item
11973// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
11974void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
11975{
11976 ImGuiContext& g = *GImGui;
11977 ImGuiWindow* window = g.CurrentWindow;
11978 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
11979 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
11980 {
11981 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!
11982 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
11983 OpenPopupEx(id, popup_flags);
11984 }
11985}
11986
11987// This is a helper to handle the simplest case of associating one named popup to one given widget.
11988// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
11989// - To create a popup with a specific identifier, pass it in str_id.
11990// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
11991// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
11992// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
11993// This is essentially the same as:
11994// id = str_id ? GetID(str_id) : GetItemID();
11995// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
11996// return BeginPopup(id);
11997// Which is essentially the same as:
11998// id = str_id ? GetID(str_id) : GetItemID();
11999// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
12000// OpenPopup(id);
12001// return BeginPopup(id);
12002// The main difference being that this is tweaked to avoid computing the ID twice.
12003bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
12004{
12005 ImGuiContext& g = *GImGui;
12006 ImGuiWindow* window = g.CurrentWindow;
12007 if (window->SkipItems)
12008 return false;
12009 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!
12010 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12011 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12012 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12013 OpenPopupEx(id, popup_flags);
12014 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12015}
12016
12017bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
12018{
12019 ImGuiContext& g = *GImGui;
12020 ImGuiWindow* window = g.CurrentWindow;
12021 if (!str_id)
12022 str_id = "window_context";
12023 ImGuiID id = window->GetID(str_id);
12024 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12025 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12026 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
12027 OpenPopupEx(id, popup_flags);
12028 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12029}
12030
12031bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
12032{
12033 ImGuiContext& g = *GImGui;
12034 ImGuiWindow* window = g.CurrentWindow;
12035 if (!str_id)
12036 str_id = "void_context";
12037 ImGuiID id = window->GetID(str_id);
12038 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12039 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
12040 if (GetTopMostPopupModal() == NULL)
12041 OpenPopupEx(id, popup_flags);
12042 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12043}
12044
12045// 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.)
12046// 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.
12047// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
12048// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
12049// this allows us to have tooltips/popups displayed out of the parent viewport.)
12050ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
12051{
12052 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
12053 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
12054 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
12055
12056 // Combo Box policy (we want a connecting edge)
12057 if (policy == ImGuiPopupPositionPolicy_ComboBox)
12058 {
12059 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
12060 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12061 {
12062 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12063 if (n != -1 && dir == *last_dir) // Already tried this direction?
12064 continue;
12065 ImVec2 pos;
12066 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
12067 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
12068 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
12069 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
12070 if (!r_outer.Contains(ImRect(pos, pos + size)))
12071 continue;
12072 *last_dir = dir;
12073 return pos;
12074 }
12075 }
12076
12077 // Tooltip and Default popup policy
12078 // (Always first try the direction we used on the last frame, if any)
12079 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
12080 {
12081 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
12082 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12083 {
12084 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12085 if (n != -1 && dir == *last_dir) // Already tried this direction?
12086 continue;
12087
12088 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);
12089 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);
12090
12091 // 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)
12092 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
12093 continue;
12094 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
12095 continue;
12096
12097 ImVec2 pos;
12098 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
12099 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
12100
12101 // Clamp top-left corner of popup
12102 pos.x = ImMax(pos.x, r_outer.Min.x);
12103 pos.y = ImMax(pos.y, r_outer.Min.y);
12104
12105 *last_dir = dir;
12106 return pos;
12107 }
12108 }
12109
12110 // Fallback when not enough room:
12111 *last_dir = ImGuiDir_None;
12112
12113 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
12114 if (policy == ImGuiPopupPositionPolicy_Tooltip)
12115 return ref_pos + ImVec2(2, 2);
12116
12117 // Otherwise try to keep within display
12118 ImVec2 pos = ref_pos;
12119 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
12120 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
12121 return pos;
12122}
12123
12124// Note that this is used for popups, which can overlap the non work-area of individual viewports.
12125ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
12126{
12127 ImGuiContext& g = *GImGui;
12128 ImRect r_screen;
12129 if (window->ViewportAllowPlatformMonitorExtend >= 0)
12130 {
12131 // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here)
12132 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend];
12133 r_screen.Min = monitor.WorkPos;
12134 r_screen.Max = monitor.WorkPos + monitor.WorkSize;
12135 }
12136 else
12137 {
12138 // Use the full viewport area (not work area) for popups
12139 r_screen = window->Viewport->GetMainRect();
12140 }
12141 ImVec2 padding = g.Style.DisplaySafeAreaPadding;
12142 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
12143 return r_screen;
12144}
12145
12146ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
12147{
12148 ImGuiContext& g = *GImGui;
12149
12150 ImRect r_outer = GetPopupAllowedExtentRect(window);
12151 if (window->Flags & ImGuiWindowFlags_ChildMenu)
12152 {
12153 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
12154 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
12155 ImGuiWindow* parent_window = window->ParentWindow;
12156 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).
12157 ImRect r_avoid;
12158 if (parent_window->DC.MenuBarAppending)
12159 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
12160 else
12161 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);
12162 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
12163 }
12164 if (window->Flags & ImGuiWindowFlags_Popup)
12165 {
12166 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
12167 }
12168 if (window->Flags & ImGuiWindowFlags_Tooltip)
12169 {
12170 // Position tooltip (always follows mouse + clamp within outer boundaries)
12171 // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position.
12172 // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin()
12173 IM_ASSERT(g.CurrentWindow == window);
12174 const float scale = g.Style.MouseCursorScale;
12175 const ImVec2 ref_pos = NavCalcPreferredRefPos();
12176 const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale;
12177 ImRect r_avoid;
12178 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
12179 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
12180 else
12181 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.
12182 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
12183 return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
12184 }
12185 IM_ASSERT(0);
12186 return window->Pos;
12187}
12188
12189//-----------------------------------------------------------------------------
12190// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
12191//-----------------------------------------------------------------------------
12192
12193// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
12194// In our terminology those should be interchangeable, yet right now this is super confusing.
12195// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
12196
12197void ImGui::SetNavWindow(ImGuiWindow* window)
12198{
12199 ImGuiContext& g = *GImGui;
12200 if (g.NavWindow != window)
12201 {
12202 IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
12203 g.NavWindow = window;
12204 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12205 }
12206 g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12207 NavUpdateAnyRequestFlag();
12208}
12209
12210void ImGui::NavHighlightActivated(ImGuiID id)
12211{
12212 ImGuiContext& g = *GImGui;
12213 g.NavHighlightActivatedId = id;
12214 g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
12215}
12216
12217void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
12218{
12219 ImGuiContext& g = *GImGui;
12220 g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
12221}
12222
12223void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
12224{
12225 ImGuiContext& g = *GImGui;
12226 IM_ASSERT(g.NavWindow != NULL);
12227 IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
12228 g.NavId = id;
12229 g.NavLayer = nav_layer;
12230 SetNavFocusScope(focus_scope_id);
12231 g.NavWindow->NavLastIds[nav_layer] = id;
12232 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
12233
12234 // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12235 NavClearPreferredPosForAxis(ImGuiAxis_X);
12236 NavClearPreferredPosForAxis(ImGuiAxis_Y);
12237}
12238
12239void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
12240{
12241 ImGuiContext& g = *GImGui;
12242 IM_ASSERT(id != 0);
12243
12244 if (g.NavWindow != window)
12245 SetNavWindow(window);
12246
12247 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
12248 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
12249 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
12250 g.NavId = id;
12251 g.NavLayer = nav_layer;
12252 SetNavFocusScope(g.CurrentFocusScopeId);
12253 window->NavLastIds[nav_layer] = id;
12254 if (g.LastItemData.ID == id)
12255 window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
12256
12257 if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
12258 g.NavDisableMouseHover = true;
12259 else
12260 g.NavDisableHighlight = true;
12261
12262 // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12263 NavClearPreferredPosForAxis(ImGuiAxis_X);
12264 NavClearPreferredPosForAxis(ImGuiAxis_Y);
12265}
12266
12267static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
12268{
12269 if (ImFabs(dx) > ImFabs(dy))
12270 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
12271 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
12272}
12273
12274static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
12275{
12276 if (cand_max < curr_min)
12277 return cand_max - curr_min;
12278 if (curr_max < cand_min)
12279 return cand_min - curr_max;
12280 return 0.0f;
12281}
12282
12283// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
12284static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
12285{
12286 ImGuiContext& g = *GImGui;
12287 ImGuiWindow* window = g.CurrentWindow;
12288 if (g.NavLayer != window->DC.NavLayerCurrent)
12289 return false;
12290
12291 // FIXME: Those are not good variables names
12292 ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle
12293 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)
12294 g.NavScoringDebugCount++;
12295
12296 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
12297 if (window->ParentWindow == g.NavWindow)
12298 {
12299 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
12300 if (!window->ClipRect.Overlaps(cand))
12301 return false;
12302 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
12303 }
12304
12305 // Compute distance between boxes
12306 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
12307 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
12308 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
12309 if (dby != 0.0f && dbx != 0.0f)
12310 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
12311 float dist_box = ImFabs(dbx) + ImFabs(dby);
12312
12313 // 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)
12314 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
12315 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
12316 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
12317
12318 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
12319 ImGuiDir quadrant;
12320 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
12321 if (dbx != 0.0f || dby != 0.0f)
12322 {
12323 // For non-overlapping boxes, use distance between boxes
12324 dax = dbx;
12325 day = dby;
12326 dist_axial = dist_box;
12327 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
12328 }
12329 else if (dcx != 0.0f || dcy != 0.0f)
12330 {
12331 // For overlapping boxes with different centers, use distance between centers
12332 dax = dcx;
12333 day = dcy;
12334 dist_axial = dist_center;
12335 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
12336 }
12337 else
12338 {
12339 // 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)
12340 quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
12341 }
12342
12343 const ImGuiDir move_dir = g.NavMoveDir;
12344#if IMGUI_DEBUG_NAV_SCORING
12345 char buf[200];
12346 if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
12347 {
12348 if (quadrant == move_dir)
12349 {
12350 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
12351 ImDrawList* draw_list = GetForegroundDrawList(window);
12352 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
12353 draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
12354 draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
12355 }
12356 }
12357 const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
12358 const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
12359 if (debug_hovering || debug_tty)
12360 {
12361 ImFormatString(buf, IM_ARRAYSIZE(buf),
12362 "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",
12363 dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
12364 if (debug_hovering)
12365 {
12366 ImDrawList* draw_list = GetForegroundDrawList(window);
12367 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
12368 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
12369 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
12370 draw_list->AddText(cand.Max, ~0U, buf);
12371 }
12372 if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
12373 }
12374#endif
12375
12376 // Is it in the quadrant we're interested in moving to?
12377 bool new_best = false;
12378 if (quadrant == move_dir)
12379 {
12380 // Does it beat the current best candidate?
12381 if (dist_box < result->DistBox)
12382 {
12383 result->DistBox = dist_box;
12384 result->DistCenter = dist_center;
12385 return true;
12386 }
12387 if (dist_box == result->DistBox)
12388 {
12389 // Try using distance between center points to break ties
12390 if (dist_center < result->DistCenter)
12391 {
12392 result->DistCenter = dist_center;
12393 new_best = true;
12394 }
12395 else if (dist_center == result->DistCenter)
12396 {
12397 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
12398 // (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),
12399 // 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.
12400 if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
12401 new_best = true;
12402 }
12403 }
12404 }
12405
12406 // 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
12407 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
12408 // 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.
12409 // 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.
12410 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
12411 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
12412 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
12413 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))
12414 {
12415 result->DistAxial = dist_axial;
12416 new_best = true;
12417 }
12418
12419 return new_best;
12420}
12421
12422static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
12423{
12424 ImGuiContext& g = *GImGui;
12425 ImGuiWindow* window = g.CurrentWindow;
12426 result->Window = window;
12427 result->ID = g.LastItemData.ID;
12428 result->FocusScopeId = g.CurrentFocusScopeId;
12429 result->InFlags = g.LastItemData.InFlags;
12430 result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
12431 if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)
12432 {
12433 IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
12434 result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
12435 }
12436}
12437
12438// True when current work location may be scrolled horizontally when moving left / right.
12439// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
12440void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
12441{
12442 ImGuiContext& g = *GImGui;
12443 ImGuiWindow* window = g.CurrentWindow;
12444 window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
12445}
12446
12447// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
12448// This is called after LastItemData is set, but NextItemData is also still valid.
12449static void ImGui::NavProcessItem()
12450{
12451 ImGuiContext& g = *GImGui;
12452 ImGuiWindow* window = g.CurrentWindow;
12453 const ImGuiID id = g.LastItemData.ID;
12454 const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
12455
12456 // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
12457 if (window->DC.NavIsScrollPushableX == false)
12458 {
12459 g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
12460 g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
12461 }
12462 const ImRect nav_bb = g.LastItemData.NavRect;
12463
12464 // Process Init Request
12465 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
12466 {
12467 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
12468 const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
12469 if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
12470 {
12471 NavApplyItemToResult(&g.NavInitResult);
12472 }
12473 if (candidate_for_nav_default_focus)
12474 {
12475 g.NavInitRequest = false; // Found a match, clear request
12476 NavUpdateAnyRequestFlag();
12477 }
12478 }
12479
12480 // Process Move Request (scoring for navigation)
12481 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
12482 if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
12483 {
12484 if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
12485 {
12486 const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
12487 if (is_tabbing)
12488 {
12489 NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
12490 }
12491 else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
12492 {
12493 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
12494 if (NavScoreItem(result))
12495 NavApplyItemToResult(result);
12496
12497 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
12498 const float VISIBLE_RATIO = 0.70f;
12499 if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
12500 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)
12501 if (NavScoreItem(&g.NavMoveResultLocalVisible))
12502 NavApplyItemToResult(&g.NavMoveResultLocalVisible);
12503 }
12504 }
12505 }
12506
12507 // Update information for currently focused/navigated item
12508 if (g.NavId == id)
12509 {
12510 if (g.NavWindow != window)
12511 SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
12512 g.NavLayer = window->DC.NavLayerCurrent;
12513 SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
12514 g.NavFocusScopeId = g.CurrentFocusScopeId;
12515 g.NavIdIsAlive = true;
12516 if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData)
12517 {
12518 IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
12519 g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
12520 }
12521 window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
12522 }
12523}
12524
12525// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
12526// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
12527// - Case 1: no nav/active id: set result to first eligible item, stop storing.
12528// - Case 2: tab forward: on ref id set counter, on counter elapse store result
12529// - 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
12530// - Case 4: tab backward: store all results, on ref id pick prev, stop storing
12531// - 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
12532void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
12533{
12534 ImGuiContext& g = *GImGui;
12535
12536 if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
12537 {
12538 if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
12539 return;
12540 if (g.NavFocusScopeId != g.CurrentFocusScopeId)
12541 return;
12542 }
12543
12544 // - Can always land on an item when using API call.
12545 // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
12546 // - Tabbing without _NavEnableKeyboard: goes through inputable items only.
12547 bool can_stop;
12548 if (move_flags & ImGuiNavMoveFlags_FocusApi)
12549 can_stop = true;
12550 else
12551 can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
12552
12553 // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
12554 ImGuiNavItemData* result = &g.NavMoveResultLocal;
12555 if (g.NavTabbingDir == +1)
12556 {
12557 // Tab Forward or SetKeyboardFocusHere() with >= 0
12558 if (can_stop && g.NavTabbingResultFirst.ID == 0)
12559 NavApplyItemToResult(&g.NavTabbingResultFirst);
12560 if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
12561 NavMoveRequestResolveWithLastItem(result);
12562 else if (g.NavId == id)
12563 g.NavTabbingCounter = 1;
12564 }
12565 else if (g.NavTabbingDir == -1)
12566 {
12567 // Tab Backward
12568 if (g.NavId == id)
12569 {
12570 if (result->ID)
12571 {
12572 g.NavMoveScoringItems = false;
12573 NavUpdateAnyRequestFlag();
12574 }
12575 }
12576 else if (can_stop)
12577 {
12578 // Keep applying until reaching NavId
12579 NavApplyItemToResult(result);
12580 }
12581 }
12582 else if (g.NavTabbingDir == 0)
12583 {
12584 if (can_stop && g.NavId == id)
12585 NavMoveRequestResolveWithLastItem(result);
12586 if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
12587 NavApplyItemToResult(&g.NavTabbingResultFirst);
12588 }
12589}
12590
12591bool ImGui::NavMoveRequestButNoResultYet()
12592{
12593 ImGuiContext& g = *GImGui;
12594 return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
12595}
12596
12597// FIXME: ScoringRect is not set
12598void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
12599{
12600 ImGuiContext& g = *GImGui;
12601 IM_ASSERT(g.NavWindow != NULL);
12602
12603 if (move_flags & ImGuiNavMoveFlags_IsTabbing)
12604 move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
12605
12606 g.NavMoveSubmitted = g.NavMoveScoringItems = true;
12607 g.NavMoveDir = move_dir;
12608 g.NavMoveDirForDebug = move_dir;
12609 g.NavMoveClipDir = clip_dir;
12610 g.NavMoveFlags = move_flags;
12611 g.NavMoveScrollFlags = scroll_flags;
12612 g.NavMoveForwardToNextFrame = false;
12613 g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
12614 g.NavMoveResultLocal.Clear();
12615 g.NavMoveResultLocalVisible.Clear();
12616 g.NavMoveResultOther.Clear();
12617 g.NavTabbingCounter = 0;
12618 g.NavTabbingResultFirst.Clear();
12619 NavUpdateAnyRequestFlag();
12620}
12621
12622void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
12623{
12624 ImGuiContext& g = *GImGui;
12625 g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
12626 NavApplyItemToResult(result);
12627 NavUpdateAnyRequestFlag();
12628}
12629
12630// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
12631void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data)
12632{
12633 ImGuiContext& g = *GImGui;
12634 g.NavMoveScoringItems = false;
12635 g.LastItemData.ID = tree_node_data->ID;
12636 g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
12637 g.LastItemData.NavRect = tree_node_data->NavRect;
12638 NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
12639 NavClearPreferredPosForAxis(ImGuiAxis_Y);
12640 NavUpdateAnyRequestFlag();
12641}
12642
12643void ImGui::NavMoveRequestCancel()
12644{
12645 ImGuiContext& g = *GImGui;
12646 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12647 NavUpdateAnyRequestFlag();
12648}
12649
12650// Forward will reuse the move request again on the next frame (generally with modifications done to it)
12651void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
12652{
12653 ImGuiContext& g = *GImGui;
12654 IM_ASSERT(g.NavMoveForwardToNextFrame == false);
12655 NavMoveRequestCancel();
12656 g.NavMoveForwardToNextFrame = true;
12657 g.NavMoveDir = move_dir;
12658 g.NavMoveClipDir = clip_dir;
12659 g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
12660 g.NavMoveScrollFlags = scroll_flags;
12661}
12662
12663// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
12664// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
12665void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
12666{
12667 ImGuiContext& g = *GImGui;
12668 IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
12669
12670 // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
12671 // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
12672 if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
12673 g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
12674}
12675
12676// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
12677// This way we could find the last focused window among our children. It would be much less confusing this way?
12678static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
12679{
12680 ImGuiWindow* parent = nav_window;
12681 while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
12682 parent = parent->ParentWindow;
12683 if (parent && parent != nav_window)
12684 parent->NavLastChildNavWindow = nav_window;
12685}
12686
12687// Restore the last focused child.
12688// Call when we are expected to land on the Main Layer (0) after FocusWindow()
12689static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
12690{
12691 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
12692 return window->NavLastChildNavWindow;
12693 if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar)
12694 if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar))
12695 return tab->Window;
12696 return window;
12697}
12698
12699void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
12700{
12701 ImGuiContext& g = *GImGui;
12702 if (layer == ImGuiNavLayer_Main)
12703 {
12704 ImGuiWindow* prev_nav_window = g.NavWindow;
12705 g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
12706 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12707 if (prev_nav_window)
12708 IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
12709 }
12710 ImGuiWindow* window = g.NavWindow;
12711 if (window->NavLastIds[layer] != 0)
12712 {
12713 SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
12714 }
12715 else
12716 {
12717 g.NavLayer = layer;
12718 NavInitWindow(window, true);
12719 }
12720}
12721
12722void ImGui::NavRestoreHighlightAfterMove()
12723{
12724 ImGuiContext& g = *GImGui;
12725 g.NavDisableHighlight = false;
12726 g.NavDisableMouseHover = g.NavMousePosDirty = true;
12727}
12728
12729static inline void ImGui::NavUpdateAnyRequestFlag()
12730{
12731 ImGuiContext& g = *GImGui;
12732 g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
12733 if (g.NavAnyRequest)
12734 IM_ASSERT(g.NavWindow != NULL);
12735}
12736
12737// This needs to be called before we submit any widget (aka in or before Begin)
12738void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
12739{
12740 // FIXME: ChildWindow test here is wrong for docking
12741 ImGuiContext& g = *GImGui;
12742 IM_ASSERT(window == g.NavWindow);
12743
12744 if (window->Flags & ImGuiWindowFlags_NoNavInputs)
12745 {
12746 g.NavId = 0;
12747 SetNavFocusScope(window->NavRootFocusScopeId);
12748 return;
12749 }
12750
12751 bool init_for_nav = false;
12752 if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
12753 init_for_nav = true;
12754 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
12755 if (init_for_nav)
12756 {
12757 SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
12758 g.NavInitRequest = true;
12759 g.NavInitRequestFromMove = false;
12760 g.NavInitResult.ID = 0;
12761 NavUpdateAnyRequestFlag();
12762 }
12763 else
12764 {
12765 g.NavId = window->NavLastIds[0];
12766 SetNavFocusScope(window->NavRootFocusScopeId);
12767 }
12768}
12769
12770static ImVec2 ImGui::NavCalcPreferredRefPos()
12771{
12772 ImGuiContext& g = *GImGui;
12773 ImGuiWindow* window = g.NavWindow;
12774 const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
12775
12776 // 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.
12777 if ((g.NavDisableHighlight || !g.NavDisableMouseHover || !window) && !activated_shortcut)
12778 {
12779 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
12780 // 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.
12781 // In theory we could move that +1.0f offset in OpenPopupEx()
12782 ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
12783 return ImVec2(p.x + 1.0f, p.y);
12784 }
12785 else
12786 {
12787 // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
12788 ImRect ref_rect;
12789 if (activated_shortcut)
12790 ref_rect = g.LastItemData.NavRect;
12791 else
12792 ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
12793
12794 // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
12795 if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
12796 {
12797 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
12798 ref_rect.Translate(window->Scroll - next_scroll);
12799 }
12800 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()));
12801 ImGuiViewport* viewport = window->Viewport;
12802 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.
12803 }
12804}
12805
12806float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
12807{
12808 ImGuiContext& g = *GImGui;
12809 float repeat_delay, repeat_rate;
12810 GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
12811
12812 ImGuiKey key_less, key_more;
12813 if (g.NavInputSource == ImGuiInputSource_Gamepad)
12814 {
12815 key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
12816 key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
12817 }
12818 else
12819 {
12820 key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
12821 key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
12822 }
12823 float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
12824 if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
12825 amount = 0.0f;
12826 return amount;
12827}
12828
12829static void ImGui::NavUpdate()
12830{
12831 ImGuiContext& g = *GImGui;
12832 ImGuiIO& io = g.IO;
12833
12834 io.WantSetMousePos = false;
12835 //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);
12836
12837 // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
12838 // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
12839 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
12840 const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
12841 if (nav_gamepad_active)
12842 for (ImGuiKey key : nav_gamepad_keys_to_change_source)
12843 if (IsKeyDown(key))
12844 g.NavInputSource = ImGuiInputSource_Gamepad;
12845 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
12846 const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
12847 if (nav_keyboard_active)
12848 for (ImGuiKey key : nav_keyboard_keys_to_change_source)
12849 if (IsKeyDown(key))
12850 g.NavInputSource = ImGuiInputSource_Keyboard;
12851
12852 // Process navigation init request (select first/default focus)
12853 g.NavJustMovedToId = 0;
12854 if (g.NavInitResult.ID != 0)
12855 NavInitRequestApplyResult();
12856 g.NavInitRequest = false;
12857 g.NavInitRequestFromMove = false;
12858 g.NavInitResult.ID = 0;
12859
12860 // Process navigation move request
12861 if (g.NavMoveSubmitted)
12862 NavMoveRequestApplyResult();
12863 g.NavTabbingCounter = 0;
12864 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12865
12866 // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
12867 bool set_mouse_pos = false;
12868 if (g.NavMousePosDirty && g.NavIdIsAlive)
12869 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
12870 set_mouse_pos = true;
12871 g.NavMousePosDirty = false;
12872 IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
12873
12874 // 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
12875 if (g.NavWindow)
12876 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
12877 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
12878 g.NavWindow->NavLastChildNavWindow = NULL;
12879
12880 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
12881 NavUpdateWindowing();
12882
12883 // Set output flags for user application
12884 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
12885 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
12886
12887 // Process NavCancel input (to close a popup, get back to parent, clear focus)
12888 NavUpdateCancelRequest();
12889
12890 // Process manual activation request
12891 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
12892 g.NavActivateFlags = ImGuiActivateFlags_None;
12893 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
12894 {
12895 const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
12896 const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
12897 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));
12898 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)));
12899 if (g.ActiveId == 0 && activate_pressed)
12900 {
12901 g.NavActivateId = g.NavId;
12902 g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
12903 }
12904 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
12905 {
12906 g.NavActivateId = g.NavId;
12907 g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
12908 }
12909 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
12910 g.NavActivateDownId = g.NavId;
12911 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
12912 {
12913 g.NavActivatePressedId = g.NavId;
12914 NavHighlightActivated(g.NavId);
12915 }
12916 }
12917 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
12918 g.NavDisableHighlight = true;
12919 if (g.NavActivateId != 0)
12920 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
12921
12922 // Highlight
12923 if (g.NavHighlightActivatedTimer > 0.0f)
12924 g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
12925 if (g.NavHighlightActivatedTimer == 0.0f)
12926 g.NavHighlightActivatedId = 0;
12927
12928 // Process programmatic activation request
12929 // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
12930 if (g.NavNextActivateId != 0)
12931 {
12932 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
12933 g.NavActivateFlags = g.NavNextActivateFlags;
12934 }
12935 g.NavNextActivateId = 0;
12936
12937 // Process move requests
12938 NavUpdateCreateMoveRequest();
12939 if (g.NavMoveDir == ImGuiDir_None)
12940 NavUpdateCreateTabbingRequest();
12941 NavUpdateAnyRequestFlag();
12942 g.NavIdIsAlive = false;
12943
12944 // Scrolling
12945 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
12946 {
12947 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
12948 ImGuiWindow* window = g.NavWindow;
12949 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.
12950 const ImGuiDir move_dir = g.NavMoveDir;
12951 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
12952 {
12953 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
12954 SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
12955 if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
12956 SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
12957 }
12958
12959 // *Normal* Manual scroll with LStick
12960 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
12961 if (nav_gamepad_active)
12962 {
12963 const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
12964 const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
12965 if (scroll_dir.x != 0.0f && window->ScrollbarX)
12966 SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
12967 if (scroll_dir.y != 0.0f)
12968 SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
12969 }
12970 }
12971
12972 // Always prioritize mouse highlight if navigation is disabled
12973 if (!nav_keyboard_active && !nav_gamepad_active)
12974 {
12975 g.NavDisableHighlight = true;
12976 g.NavDisableMouseHover = set_mouse_pos = false;
12977 }
12978
12979 // Update mouse position if requested
12980 // (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)
12981 if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
12982 TeleportMousePos(NavCalcPreferredRefPos());
12983
12984 // [DEBUG]
12985 g.NavScoringDebugCount = 0;
12986#if IMGUI_DEBUG_NAV_RECTS
12987 if (ImGuiWindow* debug_window = g.NavWindow)
12988 {
12989 ImDrawList* draw_list = GetForegroundDrawList(debug_window);
12990 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)); }
12991 //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); }
12992 }
12993#endif
12994}
12995
12996void ImGui::NavInitRequestApplyResult()
12997{
12998 // 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)
12999 ImGuiContext& g = *GImGui;
13000 if (!g.NavWindow)
13001 return;
13002
13003 ImGuiNavItemData* result = &g.NavInitResult;
13004 if (g.NavId != result->ID)
13005 {
13006 g.NavJustMovedToId = result->ID;
13007 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13008 g.NavJustMovedToKeyMods = 0;
13009 }
13010
13011 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
13012 // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
13013 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13014 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13015 g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
13016 if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13017 g.NavLastValidSelectionUserData = result->SelectionUserData;
13018 if (g.NavInitRequestFromMove)
13019 NavRestoreHighlightAfterMove();
13020}
13021
13022// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
13023static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
13024{
13025 // Bias initial rect
13026 ImGuiContext& g = *GImGui;
13027 const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
13028
13029 // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
13030 // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
13031 // - But each successful move sets new bias on one axis, only cleared when using mouse.
13032 if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
13033 {
13034 if (preferred_pos_rel.x == FLT_MAX)
13035 preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
13036 if (preferred_pos_rel.y == FLT_MAX)
13037 preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
13038 }
13039
13040 // Apply general bias on the other axis
13041 if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
13042 r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
13043 else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
13044 r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
13045}
13046
13047void ImGui::NavUpdateCreateMoveRequest()
13048{
13049 ImGuiContext& g = *GImGui;
13050 ImGuiIO& io = g.IO;
13051 ImGuiWindow* window = g.NavWindow;
13052 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13053 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13054
13055 if (g.NavMoveForwardToNextFrame && window != NULL)
13056 {
13057 // 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)
13058 // (preserve most state, which were already set by the NavMoveRequestForward() function)
13059 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
13060 IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
13061 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
13062 }
13063 else
13064 {
13065 // Initiate directional inputs request
13066 g.NavMoveDir = ImGuiDir_None;
13067 g.NavMoveFlags = ImGuiNavMoveFlags_None;
13068 g.NavMoveScrollFlags = ImGuiScrollFlags_None;
13069 if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
13070 {
13071 const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
13072 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; }
13073 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; }
13074 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; }
13075 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; }
13076 }
13077 g.NavMoveClipDir = g.NavMoveDir;
13078 g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
13079 }
13080
13081 // Update PageUp/PageDown/Home/End scroll
13082 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
13083 float scoring_rect_offset_y = 0.0f;
13084 if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
13085 scoring_rect_offset_y = NavUpdatePageUpPageDown();
13086 if (scoring_rect_offset_y != 0.0f)
13087 {
13088 g.NavScoringNoClipRect = window->InnerRect;
13089 g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
13090 }
13091
13092 // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
13093#if IMGUI_DEBUG_NAV_SCORING
13094 //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
13095 // g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
13096 if (io.KeyCtrl)
13097 {
13098 if (g.NavMoveDir == ImGuiDir_None)
13099 g.NavMoveDir = g.NavMoveDirForDebug;
13100 g.NavMoveClipDir = g.NavMoveDir;
13101 g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
13102 }
13103#endif
13104
13105 // Submit
13106 g.NavMoveForwardToNextFrame = false;
13107 if (g.NavMoveDir != ImGuiDir_None)
13108 NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
13109
13110 // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
13111 if (g.NavMoveSubmitted && g.NavId == 0)
13112 {
13113 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
13114 g.NavInitRequest = g.NavInitRequestFromMove = true;
13115 g.NavInitResult.ID = 0;
13116 g.NavDisableHighlight = false;
13117 }
13118
13119 // When using gamepad, we project the reference nav bounding box into window visible area.
13120 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
13121 // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
13122 if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
13123 {
13124 bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
13125 bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
13126 ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
13127
13128 // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
13129 // Otherwise 'inner_rect_rel' would be off on the move result frame.
13130 inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
13131
13132 if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
13133 {
13134 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
13135 float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f);
13136 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
13137 inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
13138 inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
13139 inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
13140 inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
13141 window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
13142 g.NavId = 0;
13143 }
13144 }
13145
13146 // 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)
13147 ImRect scoring_rect;
13148 if (window != NULL)
13149 {
13150 ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
13151 scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
13152 scoring_rect.TranslateY(scoring_rect_offset_y);
13153 if (g.NavMoveSubmitted)
13154 NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
13155 IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
13156 //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
13157 //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
13158 }
13159 g.NavScoringRect = scoring_rect;
13160 g.NavScoringNoClipRect.Add(scoring_rect);
13161}
13162
13163void ImGui::NavUpdateCreateTabbingRequest()
13164{
13165 ImGuiContext& g = *GImGui;
13166 ImGuiWindow* window = g.NavWindow;
13167 IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
13168 if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
13169 return;
13170
13171 const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
13172 if (!tab_pressed)
13173 return;
13174
13175 // Initiate tabbing request
13176 // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
13177 // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
13178 const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13179 if (nav_keyboard_active)
13180 g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1;
13181 else
13182 g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
13183 ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
13184 ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
13185 ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
13186 NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
13187 g.NavTabbingCounter = -1;
13188}
13189
13190// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
13191void ImGui::NavMoveRequestApplyResult()
13192{
13193 ImGuiContext& g = *GImGui;
13194#if IMGUI_DEBUG_NAV_SCORING
13195 if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
13196 return;
13197#endif
13198
13199 // Select which result to use
13200 ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
13201
13202 // Tabbing forward wrap
13203 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
13204 if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
13205 result = &g.NavTabbingResultFirst;
13206
13207 // 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)
13208 const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
13209 if (result == NULL)
13210 {
13211 if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13212 g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight;
13213 if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
13214 NavRestoreHighlightAfterMove();
13215 NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
13216 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
13217 return;
13218 }
13219
13220 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
13221 if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
13222 if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
13223 result = &g.NavMoveResultLocalVisible;
13224
13225 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
13226 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
13227 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
13228 result = &g.NavMoveResultOther;
13229 IM_ASSERT(g.NavWindow && result->Window);
13230
13231 // Scroll to keep newly navigated item fully into view.
13232 if (g.NavLayer == ImGuiNavLayer_Main)
13233 {
13234 ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
13235 ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
13236
13237 if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
13238 {
13239 // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
13240 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
13241 SetScrollY(result->Window, scroll_target);
13242 }
13243 }
13244
13245 if (g.NavWindow != result->Window)
13246 {
13247 IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
13248 g.NavWindow = result->Window;
13249 g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13250 }
13251
13252 // Clear active id unless requested not to
13253 // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
13254 // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
13255 if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
13256 ClearActiveID();
13257
13258 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
13259 // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
13260 if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
13261 {
13262 g.NavJustMovedToId = result->ID;
13263 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13264 g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
13265 }
13266
13267 // Apply new NavID/Focus
13268 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13269 ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
13270 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13271 if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13272 g.NavLastValidSelectionUserData = result->SelectionUserData;
13273
13274 // Restore last preferred position for current axis
13275 // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
13276 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
13277 {
13278 preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
13279 g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
13280 }
13281
13282 // Tabbing: Activates Inputable, otherwise only Focus
13283 if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0)
13284 g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
13285
13286 // Activate
13287 if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
13288 {
13289 g.NavNextActivateId = result->ID;
13290 g.NavNextActivateFlags = ImGuiActivateFlags_None;
13291 if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13292 g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
13293 }
13294
13295 // Enable nav highlight
13296 if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)
13297 NavRestoreHighlightAfterMove();
13298}
13299
13300// Process NavCancel input (to close a popup, get back to parent, clear focus)
13301// FIXME: In order to support e.g. Escape to clear a selection we'll need:
13302// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
13303// - 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
13304static void ImGui::NavUpdateCancelRequest()
13305{
13306 ImGuiContext& g = *GImGui;
13307 const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13308 const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13309 if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
13310 return;
13311
13312 IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
13313 if (g.ActiveId != 0)
13314 {
13315 ClearActiveID();
13316 }
13317 else if (g.NavLayer != ImGuiNavLayer_Main)
13318 {
13319 // Leave the "menu" layer
13320 NavRestoreLayer(ImGuiNavLayer_Main);
13321 NavRestoreHighlightAfterMove();
13322 }
13323 else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
13324 {
13325 // Exit child window
13326 ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
13327 ImGuiWindow* parent_window = child_window->ParentWindow;
13328 IM_ASSERT(child_window->ChildId != 0);
13329 FocusWindow(parent_window);
13330 SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
13331 NavRestoreHighlightAfterMove();
13332 }
13333 else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
13334 {
13335 // Close open popup/menu
13336 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
13337 }
13338 else
13339 {
13340 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
13341 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
13342 g.NavWindow->NavLastIds[0] = 0;
13343 g.NavId = 0;
13344 }
13345}
13346
13347// Handle PageUp/PageDown/Home/End keys
13348// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
13349// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
13350// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
13351static float ImGui::NavUpdatePageUpPageDown()
13352{
13353 ImGuiContext& g = *GImGui;
13354 ImGuiWindow* window = g.NavWindow;
13355 if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
13356 return 0.0f;
13357
13358 const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
13359 const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
13360 const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
13361 const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
13362 if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
13363 return 0.0f;
13364
13365 if (g.NavLayer != ImGuiNavLayer_Main)
13366 NavRestoreLayer(ImGuiNavLayer_Main);
13367
13368 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)
13369 {
13370 // Fallback manual-scroll when window has no navigable item
13371 if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
13372 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
13373 else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
13374 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
13375 else if (home_pressed)
13376 SetScrollY(window, 0.0f);
13377 else if (end_pressed)
13378 SetScrollY(window, window->ScrollMax.y);
13379 }
13380 else
13381 {
13382 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
13383 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
13384 float nav_scoring_rect_offset_y = 0.0f;
13385 if (IsKeyPressed(ImGuiKey_PageUp, true))
13386 {
13387 nav_scoring_rect_offset_y = -page_offset_y;
13388 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)
13389 g.NavMoveClipDir = ImGuiDir_Up;
13390 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
13391 }
13392 else if (IsKeyPressed(ImGuiKey_PageDown, true))
13393 {
13394 nav_scoring_rect_offset_y = +page_offset_y;
13395 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)
13396 g.NavMoveClipDir = ImGuiDir_Down;
13397 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
13398 }
13399 else if (home_pressed)
13400 {
13401 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
13402 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
13403 // Preserve current horizontal position if we have any.
13404 nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
13405 if (nav_rect_rel.IsInverted())
13406 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
13407 g.NavMoveDir = ImGuiDir_Down;
13408 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
13409 // FIXME-NAV: MoveClipDir left to _None, intentional?
13410 }
13411 else if (end_pressed)
13412 {
13413 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
13414 if (nav_rect_rel.IsInverted())
13415 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
13416 g.NavMoveDir = ImGuiDir_Up;
13417 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
13418 // FIXME-NAV: MoveClipDir left to _None, intentional?
13419 }
13420 return nav_scoring_rect_offset_y;
13421 }
13422 return 0.0f;
13423}
13424
13425static void ImGui::NavEndFrame()
13426{
13427 ImGuiContext& g = *GImGui;
13428
13429 // Show CTRL+TAB list window
13430 if (g.NavWindowingTarget != NULL)
13431 NavUpdateWindowingOverlay();
13432
13433 // Perform wrap-around in menus
13434 // 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.
13435 // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
13436 if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
13437 NavUpdateCreateWrappingRequest();
13438}
13439
13440static void ImGui::NavUpdateCreateWrappingRequest()
13441{
13442 ImGuiContext& g = *GImGui;
13443 ImGuiWindow* window = g.NavWindow;
13444
13445 bool do_forward = false;
13446 ImRect bb_rel = window->NavRectRel[g.NavLayer];
13447 ImGuiDir clip_dir = g.NavMoveDir;
13448
13449 const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
13450 //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
13451 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
13452 {
13453 bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
13454 if (move_flags & ImGuiNavMoveFlags_WrapX)
13455 {
13456 bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
13457 clip_dir = ImGuiDir_Up;
13458 }
13459 do_forward = true;
13460 }
13461 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
13462 {
13463 bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
13464 if (move_flags & ImGuiNavMoveFlags_WrapX)
13465 {
13466 bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
13467 clip_dir = ImGuiDir_Down;
13468 }
13469 do_forward = true;
13470 }
13471 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
13472 {
13473 bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
13474 if (move_flags & ImGuiNavMoveFlags_WrapY)
13475 {
13476 bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
13477 clip_dir = ImGuiDir_Left;
13478 }
13479 do_forward = true;
13480 }
13481 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
13482 {
13483 bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
13484 if (move_flags & ImGuiNavMoveFlags_WrapY)
13485 {
13486 bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
13487 clip_dir = ImGuiDir_Right;
13488 }
13489 do_forward = true;
13490 }
13491 if (!do_forward)
13492 return;
13493 window->NavRectRel[g.NavLayer] = bb_rel;
13494 NavClearPreferredPosForAxis(ImGuiAxis_X);
13495 NavClearPreferredPosForAxis(ImGuiAxis_Y);
13496 NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
13497}
13498
13499static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
13500{
13501 ImGuiContext& g = *GImGui;
13502 IM_UNUSED(g);
13503 int order = window->FocusOrder;
13504 IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
13505 IM_ASSERT(g.WindowsFocusOrder[order] == window);
13506 return order;
13507}
13508
13509static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
13510{
13511 ImGuiContext& g = *GImGui;
13512 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
13513 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
13514 return g.WindowsFocusOrder[i];
13515 return NULL;
13516}
13517
13518static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
13519{
13520 ImGuiContext& g = *GImGui;
13521 IM_ASSERT(g.NavWindowingTarget);
13522 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
13523 return;
13524
13525 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
13526 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
13527 if (!window_target)
13528 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
13529 if (window_target) // Don't reset windowing target if there's a single window in the list
13530 {
13531 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
13532 g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
13533 }
13534 g.NavWindowingToggleLayer = false;
13535}
13536
13537// Windowing management mode
13538// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
13539// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
13540static void ImGui::NavUpdateWindowing()
13541{
13542 ImGuiContext& g = *GImGui;
13543 ImGuiIO& io = g.IO;
13544
13545 ImGuiWindow* apply_focus_window = NULL;
13546 bool apply_toggle_layer = false;
13547
13548 ImGuiWindow* modal_window = GetTopMostPopupModal();
13549 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.
13550 if (!allow_windowing)
13551 g.NavWindowingTarget = NULL;
13552
13553 // Fade out
13554 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
13555 {
13556 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
13557 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
13558 g.NavWindowingTargetAnim = NULL;
13559 }
13560
13561 // Start CTRL+Tab or Square+L/R window selection
13562 // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
13563 const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing");
13564 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13565 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13566 const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
13567 const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
13568 const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_None);
13569 const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
13570 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
13571 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
13572 {
13573 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
13574 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
13575 g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
13576 g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
13577 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
13578
13579 // 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.
13580 if (keyboard_next_window || keyboard_prev_window)
13581 SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
13582 }
13583
13584 // Gamepad update
13585 g.NavWindowingTimer += io.DeltaTime;
13586 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
13587 {
13588 // 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
13589 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
13590
13591 // Select window to focus
13592 const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
13593 if (focus_change_dir != 0)
13594 {
13595 NavUpdateWindowingHighlightWindow(focus_change_dir);
13596 g.NavWindowingHighlightAlpha = 1.0f;
13597 }
13598
13599 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
13600 if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
13601 {
13602 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
13603 if (g.NavWindowingToggleLayer && g.NavWindow)
13604 apply_toggle_layer = true;
13605 else if (!g.NavWindowingToggleLayer)
13606 apply_focus_window = g.NavWindowingTarget;
13607 g.NavWindowingTarget = NULL;
13608 }
13609 }
13610
13611 // Keyboard: Focus
13612 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
13613 {
13614 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
13615 ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
13616 IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
13617 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
13618 if (keyboard_next_window || keyboard_prev_window)
13619 NavUpdateWindowingHighlightWindow(keyboard_next_window ? -1 : +1);
13620 else if ((io.KeyMods & shared_mods) != shared_mods)
13621 apply_focus_window = g.NavWindowingTarget;
13622 }
13623
13624 // Keyboard: Press and Release ALT to toggle menu layer
13625 const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
13626 for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
13627 if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
13628 {
13629 g.NavWindowingToggleLayer = true;
13630 g.NavWindowingToggleKey = windowing_toggle_key;
13631 g.NavInputSource = ImGuiInputSource_Keyboard;
13632 break;
13633 }
13634 if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
13635 {
13636 // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
13637 // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
13638 // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
13639 // We cancel toggling nav layer if an owner has claimed the key.
13640 if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
13641 g.NavWindowingToggleLayer = false;
13642 if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
13643 g.NavWindowingToggleLayer = false;
13644
13645 // Apply layer toggle on Alt release
13646 // 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.
13647 if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
13648 if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
13649 if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
13650 apply_toggle_layer = true;
13651 if (!IsKeyDown(g.NavWindowingToggleKey))
13652 g.NavWindowingToggleLayer = false;
13653 }
13654
13655 // Move window
13656 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
13657 {
13658 ImVec2 nav_move_dir;
13659 if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
13660 nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
13661 if (g.NavInputSource == ImGuiInputSource_Gamepad)
13662 nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
13663 if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
13664 {
13665 const float NAV_MOVE_SPEED = 800.0f;
13666 const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
13667 g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
13668 g.NavDisableMouseHover = true;
13669 ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
13670 if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
13671 {
13672 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree;
13673 SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
13674 g.NavWindowingAccumDeltaPos -= accum_floored;
13675 }
13676 }
13677 }
13678
13679 // Apply final focus
13680 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
13681 {
13682 // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow()
13683 // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow()
13684 ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL;
13685 ClearActiveID();
13686 NavRestoreHighlightAfterMove();
13687 ClosePopupsOverWindow(apply_focus_window, false);
13688 FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
13689 apply_focus_window = g.NavWindow;
13690 if (apply_focus_window->NavLastIds[0] == 0)
13691 NavInitWindow(apply_focus_window, false);
13692
13693 // If the window has ONLY a menu layer (no main layer), select it directly
13694 // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
13695 // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
13696 // the target window as already been previewed once.
13697 // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
13698 // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
13699 // won't be valid.
13700 if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
13701 g.NavLayer = ImGuiNavLayer_Menu;
13702
13703 // Request OS level focus
13704 if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus)
13705 g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport);
13706 }
13707 if (apply_focus_window)
13708 g.NavWindowingTarget = NULL;
13709
13710 // Apply menu/layer toggle
13711 if (apply_toggle_layer && g.NavWindow)
13712 {
13713 ClearActiveID();
13714
13715 // Move to parent menu if necessary
13716 ImGuiWindow* new_nav_window = g.NavWindow;
13717 while (new_nav_window->ParentWindow
13718 && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
13719 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
13720 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
13721 new_nav_window = new_nav_window->ParentWindow;
13722 if (new_nav_window != g.NavWindow)
13723 {
13724 ImGuiWindow* old_nav_window = g.NavWindow;
13725 FocusWindow(new_nav_window);
13726 new_nav_window->NavLastChildNavWindow = old_nav_window;
13727 }
13728
13729 // Toggle layer
13730 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
13731 if (new_nav_layer != g.NavLayer)
13732 {
13733 // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
13734 const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
13735 if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id)
13736 g.NavWindow->NavLastIds[new_nav_layer] = 0;
13737 NavRestoreLayer(new_nav_layer);
13738 NavRestoreHighlightAfterMove();
13739 }
13740 }
13741}
13742
13743// Window has already passed the IsWindowNavFocusable()
13744static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
13745{
13746 if (window->Flags & ImGuiWindowFlags_Popup)
13747 return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
13748 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
13749 return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
13750 if (window->DockNodeAsHost)
13751 return "(Dock node)"; // Not normally shown to user.
13752 return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
13753}
13754
13755// Overlay displayed when using CTRL+TAB. Called by EndFrame().
13756void ImGui::NavUpdateWindowingOverlay()
13757{
13758 ImGuiContext& g = *GImGui;
13759 IM_ASSERT(g.NavWindowingTarget != NULL);
13760
13761 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
13762 return;
13763
13764 if (g.NavWindowingListWindow == NULL)
13765 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
13766 const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport();
13767 SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
13768 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
13769 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
13770 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
13771 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
13772 {
13773 ImGuiWindow* window = g.WindowsFocusOrder[n];
13774 IM_ASSERT(window != NULL); // Fix static analyzers
13775 if (!IsWindowNavFocusable(window))
13776 continue;
13777 const char* label = window->Name;
13778 if (label == FindRenderedTextEnd(label))
13779 label = GetFallbackWindowNameForWindowingList(window);
13780 Selectable(label, g.NavWindowingTarget == window);
13781 }
13782 End();
13783 PopStyleVar();
13784}
13785
13786
13787//-----------------------------------------------------------------------------
13788// [SECTION] DRAG AND DROP
13789//-----------------------------------------------------------------------------
13790
13791bool ImGui::IsDragDropActive()
13792{
13793 ImGuiContext& g = *GImGui;
13794 return g.DragDropActive;
13795}
13796
13797void ImGui::ClearDragDrop()
13798{
13799 ImGuiContext& g = *GImGui;
13800 g.DragDropActive = false;
13801 g.DragDropPayload.Clear();
13802 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
13803 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
13804 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
13805 g.DragDropAcceptFrameCount = -1;
13806
13807 g.DragDropPayloadBufHeap.clear();
13808 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
13809}
13810
13811bool ImGui::BeginTooltipHidden()
13812{
13813 ImGuiContext& g = *GImGui;
13814 bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
13815 SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
13816 return ret;
13817}
13818
13819// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
13820// If the item has an identifier:
13821// - This assume/require the item to be activated (typically via ButtonBehavior).
13822// - 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.
13823// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
13824// If the item has no identifier:
13825// - Currently always assume left mouse button.
13826bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
13827{
13828 ImGuiContext& g = *GImGui;
13829 ImGuiWindow* window = g.CurrentWindow;
13830
13831 // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
13832 // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
13833 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
13834
13835 bool source_drag_active = false;
13836 ImGuiID source_id = 0;
13837 ImGuiID source_parent_id = 0;
13838 if (!(flags & ImGuiDragDropFlags_SourceExtern))
13839 {
13840 source_id = g.LastItemData.ID;
13841 if (source_id != 0)
13842 {
13843 // Common path: items with ID
13844 if (g.ActiveId != source_id)
13845 return false;
13846 if (g.ActiveIdMouseButton != -1)
13847 mouse_button = g.ActiveIdMouseButton;
13848 if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
13849 return false;
13850 g.ActiveIdAllowOverlap = false;
13851 }
13852 else
13853 {
13854 // Uncommon path: items without ID
13855 if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
13856 return false;
13857 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
13858 return false;
13859
13860 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
13861 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
13862 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
13863 {
13864 IM_ASSERT(0);
13865 return false;
13866 }
13867
13868 // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
13869 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
13870 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
13871 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
13872 // Rely on keeping other window->LastItemXXX fields intact.
13873 source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
13874 KeepAliveID(source_id);
13875 bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags);
13876 if (is_hovered && g.IO.MouseClicked[mouse_button])
13877 {
13878 SetActiveID(source_id, window);
13879 FocusWindow(window);
13880 }
13881 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
13882 g.ActiveIdAllowOverlap = is_hovered;
13883 }
13884 if (g.ActiveId != source_id)
13885 return false;
13886 source_parent_id = window->IDStack.back();
13887 source_drag_active = IsMouseDragging(mouse_button);
13888
13889 // Disable navigation and key inputs while dragging + cancel existing request if any
13890 SetActiveIdUsingAllKeyboardKeys();
13891 }
13892 else
13893 {
13894 window = NULL;
13895 source_id = ImHashStr("#SourceExtern");
13896 source_drag_active = true;
13897 }
13898
13899 IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
13900 if (source_drag_active)
13901 {
13902 if (!g.DragDropActive)
13903 {
13904 IM_ASSERT(source_id != 0);
13905 ClearDragDrop();
13906 ImGuiPayload& payload = g.DragDropPayload;
13907 payload.SourceId = source_id;
13908 payload.SourceParentId = source_parent_id;
13909 g.DragDropActive = true;
13910 g.DragDropSourceFlags = flags;
13911 g.DragDropMouseButton = mouse_button;
13912 if (payload.SourceId == g.ActiveId)
13913 g.ActiveIdNoClearOnFocusLoss = true;
13914 }
13915 g.DragDropSourceFrameCount = g.FrameCount;
13916 g.DragDropWithinSource = true;
13917
13918 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
13919 {
13920 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
13921 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
13922 bool ret;
13923 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
13924 ret = BeginTooltipHidden();
13925 else
13926 ret = BeginTooltip();
13927 IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
13928 IM_UNUSED(ret);
13929 }
13930
13931 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
13932 g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
13933
13934 return true;
13935 }
13936 return false;
13937}
13938
13939void ImGui::EndDragDropSource()
13940{
13941 ImGuiContext& g = *GImGui;
13942 IM_ASSERT(g.DragDropActive);
13943 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
13944
13945 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
13946 EndTooltip();
13947
13948 // Discard the drag if have not called SetDragDropPayload()
13949 if (g.DragDropPayload.DataFrameCount == -1)
13950 ClearDragDrop();
13951 g.DragDropWithinSource = false;
13952}
13953
13954// Use 'cond' to choose to submit payload on drag start or every frame
13955bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
13956{
13957 ImGuiContext& g = *GImGui;
13958 ImGuiPayload& payload = g.DragDropPayload;
13959 if (cond == 0)
13960 cond = ImGuiCond_Always;
13961
13962 IM_ASSERT(type != NULL);
13963 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
13964 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
13965 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
13966 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
13967
13968 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
13969 {
13970 // Copy payload
13971 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
13972 g.DragDropPayloadBufHeap.resize(0);
13973 if (data_size > sizeof(g.DragDropPayloadBufLocal))
13974 {
13975 // Store in heap
13976 g.DragDropPayloadBufHeap.resize((int)data_size);
13977 payload.Data = g.DragDropPayloadBufHeap.Data;
13978 memcpy(payload.Data, data, data_size);
13979 }
13980 else if (data_size > 0)
13981 {
13982 // Store locally
13983 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
13984 payload.Data = g.DragDropPayloadBufLocal;
13985 memcpy(payload.Data, data, data_size);
13986 }
13987 else
13988 {
13989 payload.Data = NULL;
13990 }
13991 payload.DataSize = (int)data_size;
13992 }
13993 payload.DataFrameCount = g.FrameCount;
13994
13995 // Return whether the payload has been accepted
13996 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
13997}
13998
13999bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
14000{
14001 ImGuiContext& g = *GImGui;
14002 if (!g.DragDropActive)
14003 return false;
14004
14005 ImGuiWindow* window = g.CurrentWindow;
14006 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14007 if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree)
14008 return false;
14009 IM_ASSERT(id != 0);
14010 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
14011 return false;
14012 if (window->SkipItems)
14013 return false;
14014
14015 IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14016 g.DragDropTargetRect = bb;
14017 g.DragDropTargetClipRect = window->ClipRect; // May want to be overriden by user depending on use case?
14018 g.DragDropTargetId = id;
14019 g.DragDropWithinTarget = true;
14020 return true;
14021}
14022
14023// We don't use BeginDragDropTargetCustom() and duplicate its code because:
14024// 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.
14025// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
14026// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
14027bool ImGui::BeginDragDropTarget()
14028{
14029 ImGuiContext& g = *GImGui;
14030 if (!g.DragDropActive)
14031 return false;
14032
14033 ImGuiWindow* window = g.CurrentWindow;
14034 if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
14035 return false;
14036 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14037 if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems)
14038 return false;
14039
14040 const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
14041 ImGuiID id = g.LastItemData.ID;
14042 if (id == 0)
14043 {
14044 id = window->GetIDFromRectangle(display_rect);
14045 KeepAliveID(id);
14046 }
14047 if (g.DragDropPayload.SourceId == id)
14048 return false;
14049
14050 IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14051 g.DragDropTargetRect = display_rect;
14052 g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
14053 g.DragDropTargetId = id;
14054 g.DragDropWithinTarget = true;
14055 return true;
14056}
14057
14058bool ImGui::IsDragDropPayloadBeingAccepted()
14059{
14060 ImGuiContext& g = *GImGui;
14061 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
14062}
14063
14064const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
14065{
14066 ImGuiContext& g = *GImGui;
14067 ImGuiPayload& payload = g.DragDropPayload;
14068 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
14069 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
14070 if (type != NULL && !payload.IsDataType(type))
14071 return NULL;
14072
14073 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
14074 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
14075 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
14076 ImRect r = g.DragDropTargetRect;
14077 float r_surface = r.GetWidth() * r.GetHeight();
14078 if (r_surface > g.DragDropAcceptIdCurrRectSurface)
14079 return NULL;
14080
14081 g.DragDropAcceptFlags = flags;
14082 g.DragDropAcceptIdCurr = g.DragDropTargetId;
14083 g.DragDropAcceptIdCurrRectSurface = r_surface;
14084 //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
14085
14086 // Render default drop visuals
14087 payload.Preview = was_accepted_previously;
14088 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
14089 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
14090 RenderDragDropTargetRect(r, g.DragDropTargetClipRect);
14091
14092 g.DragDropAcceptFrameCount = g.FrameCount;
14093 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()
14094 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
14095 return NULL;
14096
14097 //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: return payload\n", g.DragDropTargetId);
14098 return &payload;
14099}
14100
14101// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
14102void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
14103{
14104 ImGuiContext& g = *GImGui;
14105 ImGuiWindow* window = g.CurrentWindow;
14106 ImRect bb_display = bb;
14107 bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
14108 bb_display.Expand(3.5f);
14109 bool push_clip_rect = !window->ClipRect.Contains(bb_display);
14110 if (push_clip_rect)
14111 window->DrawList->PushClipRectFullScreen();
14112 window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
14113 if (push_clip_rect)
14114 window->DrawList->PopClipRect();
14115}
14116
14117const ImGuiPayload* ImGui::GetDragDropPayload()
14118{
14119 ImGuiContext& g = *GImGui;
14120 return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
14121}
14122
14123void ImGui::EndDragDropTarget()
14124{
14125 ImGuiContext& g = *GImGui;
14126 IM_ASSERT(g.DragDropActive);
14127 IM_ASSERT(g.DragDropWithinTarget);
14128 g.DragDropWithinTarget = false;
14129
14130 // Clear drag and drop state payload right after delivery
14131 if (g.DragDropPayload.Delivery)
14132 ClearDragDrop();
14133}
14134
14135//-----------------------------------------------------------------------------
14136// [SECTION] LOGGING/CAPTURING
14137//-----------------------------------------------------------------------------
14138// All text output from the interface can be captured into tty/file/clipboard.
14139// By default, tree nodes are automatically opened during logging.
14140//-----------------------------------------------------------------------------
14141
14142// Pass text data straight to log (without being displayed)
14143static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
14144{
14145 if (g.LogFile)
14146 {
14147 g.LogBuffer.Buf.resize(0);
14148 g.LogBuffer.appendfv(fmt, args);
14149 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
14150 }
14151 else
14152 {
14153 g.LogBuffer.appendfv(fmt, args);
14154 }
14155}
14156
14157void ImGui::LogText(const char* fmt, ...)
14158{
14159 ImGuiContext& g = *GImGui;
14160 if (!g.LogEnabled)
14161 return;
14162
14163 va_list args;
14164 va_start(args, fmt);
14165 LogTextV(g, fmt, args);
14166 va_end(args);
14167}
14168
14169void ImGui::LogTextV(const char* fmt, va_list args)
14170{
14171 ImGuiContext& g = *GImGui;
14172 if (!g.LogEnabled)
14173 return;
14174
14175 LogTextV(g, fmt, args);
14176}
14177
14178// Internal version that takes a position to decide on newline placement and pad items according to their depth.
14179// We split text into individual lines to add current tree level padding
14180// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
14181void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
14182{
14183 ImGuiContext& g = *GImGui;
14184 ImGuiWindow* window = g.CurrentWindow;
14185
14186 const char* prefix = g.LogNextPrefix;
14187 const char* suffix = g.LogNextSuffix;
14188 g.LogNextPrefix = g.LogNextSuffix = NULL;
14189
14190 if (!text_end)
14191 text_end = FindRenderedTextEnd(text, text_end);
14192
14193 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
14194 if (ref_pos)
14195 g.LogLinePosY = ref_pos->y;
14196 if (log_new_line)
14197 {
14198 LogText(IM_NEWLINE);
14199 g.LogLineFirstItem = true;
14200 }
14201
14202 if (prefix)
14203 LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
14204
14205 // Re-adjust padding if we have popped out of our starting depth
14206 if (g.LogDepthRef > window->DC.TreeDepth)
14207 g.LogDepthRef = window->DC.TreeDepth;
14208 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
14209
14210 const char* text_remaining = text;
14211 for (;;)
14212 {
14213 // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
14214 // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
14215 const char* line_start = text_remaining;
14216 const char* line_end = ImStreolRange(line_start, text_end);
14217 const bool is_last_line = (line_end == text_end);
14218 if (line_start != line_end || !is_last_line)
14219 {
14220 const int line_length = (int)(line_end - line_start);
14221 const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
14222 LogText("%*s%.*s", indentation, "", line_length, line_start);
14223 g.LogLineFirstItem = false;
14224 if (*line_end == '\n')
14225 {
14226 LogText(IM_NEWLINE);
14227 g.LogLineFirstItem = true;
14228 }
14229 }
14230 if (is_last_line)
14231 break;
14232 text_remaining = line_end + 1;
14233 }
14234
14235 if (suffix)
14236 LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
14237}
14238
14239// Start logging/capturing text output
14240void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
14241{
14242 ImGuiContext& g = *GImGui;
14243 ImGuiWindow* window = g.CurrentWindow;
14244 IM_ASSERT(g.LogEnabled == false);
14245 IM_ASSERT(g.LogFile == NULL);
14246 IM_ASSERT(g.LogBuffer.empty());
14247 g.LogEnabled = true;
14248 g.LogType = type;
14249 g.LogNextPrefix = g.LogNextSuffix = NULL;
14250 g.LogDepthRef = window->DC.TreeDepth;
14251 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
14252 g.LogLinePosY = FLT_MAX;
14253 g.LogLineFirstItem = true;
14254}
14255
14256// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
14257void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
14258{
14259 ImGuiContext& g = *GImGui;
14260 g.LogNextPrefix = prefix;
14261 g.LogNextSuffix = suffix;
14262}
14263
14264void ImGui::LogToTTY(int auto_open_depth)
14265{
14266 ImGuiContext& g = *GImGui;
14267 if (g.LogEnabled)
14268 return;
14269 IM_UNUSED(auto_open_depth);
14270#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
14271 LogBegin(ImGuiLogType_TTY, auto_open_depth);
14272 g.LogFile = stdout;
14273#endif
14274}
14275
14276// Start logging/capturing text output to given file
14277void ImGui::LogToFile(int auto_open_depth, const char* filename)
14278{
14279 ImGuiContext& g = *GImGui;
14280 if (g.LogEnabled)
14281 return;
14282
14283 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
14284 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
14285 // By opening the file in binary mode "ab" we have consistent output everywhere.
14286 if (!filename)
14287 filename = g.IO.LogFilename;
14288 if (!filename || !filename[0])
14289 return;
14290 ImFileHandle f = ImFileOpen(filename, "ab");
14291 if (!f)
14292 {
14293 IM_ASSERT(0);
14294 return;
14295 }
14296
14297 LogBegin(ImGuiLogType_File, auto_open_depth);
14298 g.LogFile = f;
14299}
14300
14301// Start logging/capturing text output to clipboard
14302void ImGui::LogToClipboard(int auto_open_depth)
14303{
14304 ImGuiContext& g = *GImGui;
14305 if (g.LogEnabled)
14306 return;
14307 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
14308}
14309
14310void ImGui::LogToBuffer(int auto_open_depth)
14311{
14312 ImGuiContext& g = *GImGui;
14313 if (g.LogEnabled)
14314 return;
14315 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
14316}
14317
14318void ImGui::LogFinish()
14319{
14320 ImGuiContext& g = *GImGui;
14321 if (!g.LogEnabled)
14322 return;
14323
14324 LogText(IM_NEWLINE);
14325 switch (g.LogType)
14326 {
14327 case ImGuiLogType_TTY:
14328#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
14329 fflush(g.LogFile);
14330#endif
14331 break;
14332 case ImGuiLogType_File:
14333 ImFileClose(g.LogFile);
14334 break;
14335 case ImGuiLogType_Buffer:
14336 break;
14337 case ImGuiLogType_Clipboard:
14338 if (!g.LogBuffer.empty())
14339 SetClipboardText(g.LogBuffer.begin());
14340 break;
14341 case ImGuiLogType_None:
14342 IM_ASSERT(0);
14343 break;
14344 }
14345
14346 g.LogEnabled = false;
14347 g.LogType = ImGuiLogType_None;
14348 g.LogFile = NULL;
14349 g.LogBuffer.clear();
14350}
14351
14352// Helper to display logging buttons
14353// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
14354void ImGui::LogButtons()
14355{
14356 ImGuiContext& g = *GImGui;
14357
14358 PushID("LogButtons");
14359#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
14360 const bool log_to_tty = Button("Log To TTY"); SameLine();
14361#else
14362 const bool log_to_tty = false;
14363#endif
14364 const bool log_to_file = Button("Log To File"); SameLine();
14365 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
14366 PushTabStop(false);
14367 SetNextItemWidth(80.0f);
14368 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
14369 PopTabStop();
14370 PopID();
14371
14372 // Start logging at the end of the function so that the buttons don't appear in the log
14373 if (log_to_tty)
14374 LogToTTY();
14375 if (log_to_file)
14376 LogToFile();
14377 if (log_to_clipboard)
14378 LogToClipboard();
14379}
14380
14381
14382//-----------------------------------------------------------------------------
14383// [SECTION] SETTINGS
14384//-----------------------------------------------------------------------------
14385// - UpdateSettings() [Internal]
14386// - MarkIniSettingsDirty() [Internal]
14387// - FindSettingsHandler() [Internal]
14388// - ClearIniSettings() [Internal]
14389// - LoadIniSettingsFromDisk()
14390// - LoadIniSettingsFromMemory()
14391// - SaveIniSettingsToDisk()
14392// - SaveIniSettingsToMemory()
14393//-----------------------------------------------------------------------------
14394// - CreateNewWindowSettings() [Internal]
14395// - FindWindowSettingsByID() [Internal]
14396// - FindWindowSettingsByWindow() [Internal]
14397// - ClearWindowSettings() [Internal]
14398// - WindowSettingsHandler_***() [Internal]
14399//-----------------------------------------------------------------------------
14400
14401// Called by NewFrame()
14402void ImGui::UpdateSettings()
14403{
14404 // Load settings on first frame (if not explicitly loaded manually before)
14405 ImGuiContext& g = *GImGui;
14406 if (!g.SettingsLoaded)
14407 {
14408 IM_ASSERT(g.SettingsWindows.empty());
14409 if (g.IO.IniFilename)
14410 LoadIniSettingsFromDisk(g.IO.IniFilename);
14411 g.SettingsLoaded = true;
14412 }
14413
14414 // Save settings (with a delay after the last modification, so we don't spam disk too much)
14415 if (g.SettingsDirtyTimer > 0.0f)
14416 {
14417 g.SettingsDirtyTimer -= g.IO.DeltaTime;
14418 if (g.SettingsDirtyTimer <= 0.0f)
14419 {
14420 if (g.IO.IniFilename != NULL)
14421 SaveIniSettingsToDisk(g.IO.IniFilename);
14422 else
14423 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
14424 g.SettingsDirtyTimer = 0.0f;
14425 }
14426 }
14427}
14428
14429void ImGui::MarkIniSettingsDirty()
14430{
14431 ImGuiContext& g = *GImGui;
14432 if (g.SettingsDirtyTimer <= 0.0f)
14433 g.SettingsDirtyTimer = g.IO.IniSavingRate;
14434}
14435
14436void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
14437{
14438 ImGuiContext& g = *GImGui;
14439 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
14440 if (g.SettingsDirtyTimer <= 0.0f)
14441 g.SettingsDirtyTimer = g.IO.IniSavingRate;
14442}
14443
14444void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
14445{
14446 ImGuiContext& g = *GImGui;
14447 IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
14448 g.SettingsHandlers.push_back(*handler);
14449}
14450
14451void ImGui::RemoveSettingsHandler(const char* type_name)
14452{
14453 ImGuiContext& g = *GImGui;
14454 if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
14455 g.SettingsHandlers.erase(handler);
14456}
14457
14458ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
14459{
14460 ImGuiContext& g = *GImGui;
14461 const ImGuiID type_hash = ImHashStr(type_name);
14462 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
14463 if (handler.TypeHash == type_hash)
14464 return &handler;
14465 return NULL;
14466}
14467
14468// Clear all settings (windows, tables, docking etc.)
14469void ImGui::ClearIniSettings()
14470{
14471 ImGuiContext& g = *GImGui;
14472 g.SettingsIniData.clear();
14473 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
14474 if (handler.ClearAllFn != NULL)
14475 handler.ClearAllFn(&g, &handler);
14476}
14477
14478void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
14479{
14480 size_t file_data_size = 0;
14481 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
14482 if (!file_data)
14483 return;
14484 if (file_data_size > 0)
14485 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
14486 IM_FREE(file_data);
14487}
14488
14489// Zero-tolerance, no error reporting, cheap .ini parsing
14490// 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!
14491void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
14492{
14493 ImGuiContext& g = *GImGui;
14494 IM_ASSERT(g.Initialized);
14495 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
14496 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
14497
14498 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
14499 // 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..
14500 if (ini_size == 0)
14501 ini_size = strlen(ini_data);
14502 g.SettingsIniData.Buf.resize((int)ini_size + 1);
14503 char* const buf = g.SettingsIniData.Buf.Data;
14504 char* const buf_end = buf + ini_size;
14505 memcpy(buf, ini_data, ini_size);
14506 buf_end[0] = 0;
14507
14508 // Call pre-read handlers
14509 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
14510 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
14511 if (handler.ReadInitFn != NULL)
14512 handler.ReadInitFn(&g, &handler);
14513
14514 void* entry_data = NULL;
14515 ImGuiSettingsHandler* entry_handler = NULL;
14516
14517 char* line_end = NULL;
14518 for (char* line = buf; line < buf_end; line = line_end + 1)
14519 {
14520 // Skip new lines markers, then find end of the line
14521 while (*line == '\n' || *line == '\r')
14522 line++;
14523 line_end = line;
14524 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
14525 line_end++;
14526 line_end[0] = 0;
14527 if (line[0] == ';')
14528 continue;
14529 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
14530 {
14531 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
14532 line_end[-1] = 0;
14533 const char* name_end = line_end - 1;
14534 const char* type_start = line + 1;
14535 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
14536 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
14537 if (!type_end || !name_start)
14538 continue;
14539 *type_end = 0; // Overwrite first ']'
14540 name_start++; // Skip second '['
14541 entry_handler = FindSettingsHandler(type_start);
14542 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
14543 }
14544 else if (entry_handler != NULL && entry_data != NULL)
14545 {
14546 // Let type handler parse the line
14547 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
14548 }
14549 }
14550 g.SettingsLoaded = true;
14551
14552 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
14553 memcpy(buf, ini_data, ini_size);
14554
14555 // Call post-read handlers
14556 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
14557 if (handler.ApplyAllFn != NULL)
14558 handler.ApplyAllFn(&g, &handler);
14559}
14560
14561void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
14562{
14563 ImGuiContext& g = *GImGui;
14564 g.SettingsDirtyTimer = 0.0f;
14565 if (!ini_filename)
14566 return;
14567
14568 size_t ini_data_size = 0;
14569 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
14570 ImFileHandle f = ImFileOpen(ini_filename, "wt");
14571 if (!f)
14572 return;
14573 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
14574 ImFileClose(f);
14575}
14576
14577// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
14578const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
14579{
14580 ImGuiContext& g = *GImGui;
14581 g.SettingsDirtyTimer = 0.0f;
14582 g.SettingsIniData.Buf.resize(0);
14583 g.SettingsIniData.Buf.push_back(0);
14584 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
14585 handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
14586 if (out_size)
14587 *out_size = (size_t)g.SettingsIniData.size();
14588 return g.SettingsIniData.c_str();
14589}
14590
14591ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
14592{
14593 ImGuiContext& g = *GImGui;
14594
14595 if (g.IO.ConfigDebugIniSettings == false)
14596 {
14597 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
14598 // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
14599 if (const char* p = strstr(name, "###"))
14600 name = p;
14601 }
14602 const size_t name_len = strlen(name);
14603
14604 // Allocate chunk
14605 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
14606 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
14607 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
14608 settings->ID = ImHashStr(name, name_len);
14609 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
14610
14611 return settings;
14612}
14613
14614// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
14615// This is called once per window .ini entry + once per newly instantiated window.
14616ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
14617{
14618 ImGuiContext& g = *GImGui;
14619 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14620 if (settings->ID == id && !settings->WantDelete)
14621 return settings;
14622 return NULL;
14623}
14624
14625// This is faster if you are holding on a Window already as we don't need to perform a search.
14626ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
14627{
14628 ImGuiContext& g = *GImGui;
14629 if (window->SettingsOffset != -1)
14630 return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
14631 return FindWindowSettingsByID(window->ID);
14632}
14633
14634// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
14635void ImGui::ClearWindowSettings(const char* name)
14636{
14637 //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
14638 ImGuiContext& g = *GImGui;
14639 ImGuiWindow* window = FindWindowByName(name);
14640 if (window != NULL)
14641 {
14642 window->Flags |= ImGuiWindowFlags_NoSavedSettings;
14643 InitOrLoadWindowSettings(window, NULL);
14644 if (window->DockId != 0)
14645 DockContextProcessUndockWindow(&g, window, true);
14646 }
14647 if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
14648 settings->WantDelete = true;
14649}
14650
14651static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
14652{
14653 ImGuiContext& g = *ctx;
14654 for (ImGuiWindow* window : g.Windows)
14655 window->SettingsOffset = -1;
14656 g.SettingsWindows.clear();
14657}
14658
14659static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
14660{
14661 ImGuiID id = ImHashStr(name);
14662 ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
14663 if (settings)
14664 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
14665 else
14666 settings = ImGui::CreateNewWindowSettings(name);
14667 settings->ID = id;
14668 settings->WantApply = true;
14669 return (void*)settings;
14670}
14671
14672static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
14673{
14674 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
14675 int x, y;
14676 int i;
14677 ImU32 u1;
14678 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
14679 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
14680 else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; }
14681 else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); }
14682 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
14683 else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); }
14684 else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; }
14685 else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; }
14686 else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; }
14687}
14688
14689// Apply to existing windows (if any)
14690static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
14691{
14692 ImGuiContext& g = *ctx;
14693 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14694 if (settings->WantApply)
14695 {
14696 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
14697 ApplyWindowSettings(window, settings);
14698 settings->WantApply = false;
14699 }
14700}
14701
14702static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
14703{
14704 // Gather data from windows that were active during this session
14705 // (if a window wasn't opened in this session we preserve its settings)
14706 ImGuiContext& g = *ctx;
14707 for (ImGuiWindow* window : g.Windows)
14708 {
14709 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
14710 continue;
14711
14712 ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
14713 if (!settings)
14714 {
14715 settings = ImGui::CreateNewWindowSettings(window->Name);
14716 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
14717 }
14718 IM_ASSERT(settings->ID == window->ID);
14719 settings->Pos = ImVec2ih(window->Pos - window->ViewportPos);
14720 settings->Size = ImVec2ih(window->SizeFull);
14721 settings->ViewportId = window->ViewportId;
14722 settings->ViewportPos = ImVec2ih(window->ViewportPos);
14723 IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
14724 settings->DockId = window->DockId;
14725 settings->ClassId = window->WindowClass.ClassId;
14726 settings->DockOrder = window->DockOrder;
14727 settings->Collapsed = window->Collapsed;
14728 settings->IsChild = (window->RootWindow != window); // Cannot rely on ImGuiWindowFlags_ChildWindow here as docked windows have this set.
14729 settings->WantDelete = false;
14730 }
14731
14732 // Write to text buffer
14733 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
14734 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
14735 {
14736 if (settings->WantDelete)
14737 continue;
14738 const char* settings_name = settings->GetName();
14739 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
14740 if (settings->IsChild)
14741 {
14742 buf->appendf("IsChild=1\n");
14743 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
14744 }
14745 else
14746 {
14747 if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
14748 {
14749 buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y);
14750 buf->appendf("ViewportId=0x%08X\n", settings->ViewportId);
14751 }
14752 if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
14753 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
14754 if (settings->Size.x != 0 || settings->Size.y != 0)
14755 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
14756 buf->appendf("Collapsed=%d\n", settings->Collapsed);
14757 if (settings->DockId != 0)
14758 {
14759 //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.
14760 if (settings->DockOrder == -1)
14761 buf->appendf("DockId=0x%08X\n", settings->DockId);
14762 else
14763 buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
14764 if (settings->ClassId != 0)
14765 buf->appendf("ClassId=0x%08X\n", settings->ClassId);
14766 }
14767 }
14768 buf->append("\n");
14769 }
14770}
14771
14772
14773//-----------------------------------------------------------------------------
14774// [SECTION] LOCALIZATION
14775//-----------------------------------------------------------------------------
14776
14777void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
14778{
14779 ImGuiContext& g = *GImGui;
14780 for (int n = 0; n < count; n++)
14781 g.LocalizationTable[entries[n].Key] = entries[n].Text;
14782}
14783
14784
14785//-----------------------------------------------------------------------------
14786// [SECTION] VIEWPORTS, PLATFORM WINDOWS
14787//-----------------------------------------------------------------------------
14788// - GetMainViewport()
14789// - FindViewportByID()
14790// - FindViewportByPlatformHandle()
14791// - SetCurrentViewport() [Internal]
14792// - SetWindowViewport() [Internal]
14793// - GetWindowAlwaysWantOwnViewport() [Internal]
14794// - UpdateTryMergeWindowIntoHostViewport() [Internal]
14795// - UpdateTryMergeWindowIntoHostViewports() [Internal]
14796// - TranslateWindowsInViewport() [Internal]
14797// - ScaleWindowsInViewport() [Internal]
14798// - FindHoveredViewportFromPlatformWindowStack() [Internal]
14799// - UpdateViewportsNewFrame() [Internal]
14800// - UpdateViewportsEndFrame() [Internal]
14801// - AddUpdateViewport() [Internal]
14802// - WindowSelectViewport() [Internal]
14803// - WindowSyncOwnedViewport() [Internal]
14804// - UpdatePlatformWindows()
14805// - RenderPlatformWindowsDefault()
14806// - FindPlatformMonitorForPos() [Internal]
14807// - FindPlatformMonitorForRect() [Internal]
14808// - UpdateViewportPlatformMonitor() [Internal]
14809// - DestroyPlatformWindow() [Internal]
14810// - DestroyPlatformWindows()
14811//-----------------------------------------------------------------------------
14812
14813ImGuiViewport* ImGui::GetMainViewport()
14814{
14815 ImGuiContext& g = *GImGui;
14816 return g.Viewports[0];
14817}
14818
14819// FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236)
14820ImGuiViewport* ImGui::FindViewportByID(ImGuiID id)
14821{
14822 ImGuiContext& g = *GImGui;
14823 for (ImGuiViewportP* viewport : g.Viewports)
14824 if (viewport->ID == id)
14825 return viewport;
14826 return NULL;
14827}
14828
14829ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle)
14830{
14831 ImGuiContext& g = *GImGui;
14832 for (ImGuiViewportP* viewport : g.Viewports)
14833 if (viewport->PlatformHandle == platform_handle)
14834 return viewport;
14835 return NULL;
14836}
14837
14838void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport)
14839{
14840 ImGuiContext& g = *GImGui;
14841 (void)current_window;
14842
14843 if (viewport)
14844 viewport->LastFrameActive = g.FrameCount;
14845 if (g.CurrentViewport == viewport)
14846 return;
14847 g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f;
14848 g.CurrentViewport = viewport;
14849 //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0);
14850
14851 // Notify platform layer of viewport changes
14852 // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI
14853 if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport)
14854 g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport);
14855}
14856
14857void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
14858{
14859 // Abandon viewport
14860 if (window->ViewportOwned && window->Viewport->Window == window)
14861 window->Viewport->Size = ImVec2(0.0f, 0.0f);
14862
14863 window->Viewport = viewport;
14864 window->ViewportId = viewport->ID;
14865 window->ViewportOwned = (viewport->Window == window);
14866}
14867
14868static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window)
14869{
14870 // 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.
14871 ImGuiContext& g = *GImGui;
14872 if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge))
14873 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
14874 if (!window->DockIsActive)
14875 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0)
14876 if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0)
14877 return true;
14878 return false;
14879}
14880
14881static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
14882{
14883 ImGuiContext& g = *GImGui;
14884 if (window->Viewport == viewport)
14885 return false;
14886 if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0)
14887 return false;
14888 if ((viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0)
14889 return false;
14890 if (!viewport->GetMainRect().Contains(window->Rect()))
14891 return false;
14892 if (GetWindowAlwaysWantOwnViewport(window))
14893 return false;
14894
14895 // 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..
14896 for (ImGuiWindow* window_behind : g.Windows)
14897 {
14898 if (window_behind == window)
14899 break;
14900 if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow))
14901 if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect()))
14902 return false;
14903 }
14904
14905 // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child)
14906 ImGuiViewportP* old_viewport = window->Viewport;
14907 if (window->ViewportOwned)
14908 for (int n = 0; n < g.Windows.Size; n++)
14909 if (g.Windows[n]->Viewport == old_viewport)
14910 SetWindowViewport(g.Windows[n], viewport);
14911 SetWindowViewport(window, viewport);
14912 BringWindowToDisplayFront(window);
14913
14914 return true;
14915}
14916
14917// FIXME: handle 0 to N host viewports
14918static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window)
14919{
14920 ImGuiContext& g = *GImGui;
14921 return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]);
14922}
14923
14924// Translate Dear ImGui windows when a Host Viewport has been moved
14925// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
14926void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos)
14927{
14928 ImGuiContext& g = *GImGui;
14929 IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows));
14930
14931 // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently
14932 // translate imgui windows from OS-window-local to absolute coordinates or vice-versa.
14933 // 2) If it's not going to fit into the new size, keep it at same absolute position.
14934 // One problem with this is that most Win32 applications doesn't update their render while dragging,
14935 // and so the window will appear to teleport when releasing the mouse.
14936 const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable);
14937 ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size);
14938 ImVec2 delta_pos = new_pos - old_pos;
14939 for (ImGuiWindow* window : g.Windows) // FIXME-OPT
14940 if (translate_all_windows || (window->Viewport == viewport && test_still_fit_rect.Contains(window->Rect())))
14941 TranslateWindow(window, delta_pos);
14942}
14943
14944// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
14945void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
14946{
14947 ImGuiContext& g = *GImGui;
14948 if (viewport->Window)
14949 {
14950 ScaleWindow(viewport->Window, scale);
14951 }
14952 else
14953 {
14954 for (ImGuiWindow* window : g.Windows)
14955 if (window->Viewport == viewport)
14956 ScaleWindow(window, scale);
14957 }
14958}
14959
14960// If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves.
14961// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
14962// B) It requires Platform_GetWindowFocus to be implemented by backend.
14963ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos)
14964{
14965 ImGuiContext& g = *GImGui;
14966 ImGuiViewportP* best_candidate = NULL;
14967 for (ImGuiViewportP* viewport : g.Viewports)
14968 if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_IsMinimized)) && viewport->GetMainRect().Contains(mouse_platform_pos))
14969 if (best_candidate == NULL || best_candidate->LastFocusedStampCount < viewport->LastFocusedStampCount)
14970 best_candidate = viewport;
14971 return best_candidate;
14972}
14973
14974// Update viewports and monitor infos
14975// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info.
14976static void ImGui::UpdateViewportsNewFrame()
14977{
14978 ImGuiContext& g = *GImGui;
14979 IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size);
14980
14981 // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport)
14982 // Update Focused status
14983 const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0;
14984 if (viewports_enabled)
14985 {
14986 ImGuiViewportP* focused_viewport = NULL;
14987 for (ImGuiViewportP* viewport : g.Viewports)
14988 {
14989 const bool platform_funcs_available = viewport->PlatformWindowCreated;
14990 if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available)
14991 {
14992 bool is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport);
14993 if (is_minimized)
14994 viewport->Flags |= ImGuiViewportFlags_IsMinimized;
14995 else
14996 viewport->Flags &= ~ImGuiViewportFlags_IsMinimized;
14997 }
14998
14999 // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport.
15000 // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored.
15001 if (g.PlatformIO.Platform_GetWindowFocus && platform_funcs_available)
15002 {
15003 bool is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport);
15004 if (is_focused)
15005 viewport->Flags |= ImGuiViewportFlags_IsFocused;
15006 else
15007 viewport->Flags &= ~ImGuiViewportFlags_IsFocused;
15008 if (is_focused)
15009 focused_viewport = viewport;
15010 }
15011 }
15012
15013 // Focused viewport has changed?
15014 if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID)
15015 {
15016 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Focused viewport changed %08X -> %08X, attempting to apply our focus.\n", g.PlatformLastFocusedViewportId, focused_viewport->ID);
15017 const ImGuiViewport* prev_focused_viewport = FindViewportByID(g.PlatformLastFocusedViewportId);
15018 const bool prev_focused_has_been_destroyed = (prev_focused_viewport == NULL) || (prev_focused_viewport->PlatformWindowCreated == false);
15019
15020 // Store a tag so we can infer z-order easily from all our windows
15021 // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag
15022 // will keep the front most stamp instead of losing it back to their parent viewport.
15023 if (focused_viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)
15024 focused_viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;
15025 g.PlatformLastFocusedViewportId = focused_viewport->ID;
15026
15027 // Focus associated dear imgui window
15028 // - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299)
15029 // - if focus didn't happen because we destroyed another window (#6462)
15030 // FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window->Window != NULL' case as well.
15031 const bool apply_imgui_focus_on_focused_viewport = !IsAnyMouseDown() && !prev_focused_has_been_destroyed;
15032 if (apply_imgui_focus_on_focused_viewport)
15033 {
15034 focused_viewport->LastFocusedHadNavWindow |= (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); // Update so a window changing viewport won't lose focus.
15035 ImGuiFocusRequestFlags focus_request_flags = ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild;
15036 if (focused_viewport->Window != NULL)
15037 FocusWindow(focused_viewport->Window, focus_request_flags);
15038 else if (focused_viewport->LastFocusedHadNavWindow)
15039 FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport, focus_request_flags); // Focus top most in viewport
15040 else
15041 FocusWindow(NULL, focus_request_flags); // No window had focus last time viewport was focused
15042 }
15043 }
15044 if (focused_viewport)
15045 focused_viewport->LastFocusedHadNavWindow = (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport);
15046 }
15047
15048 // Create/update main viewport with current platform position.
15049 // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
15050 ImGuiViewportP* main_viewport = g.Viewports[0];
15051 IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID);
15052 IM_ASSERT(main_viewport->Window == NULL);
15053 ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f);
15054 ImVec2 main_viewport_size = g.IO.DisplaySize;
15055 if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_IsMinimized))
15056 {
15057 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)
15058 main_viewport_size = main_viewport->Size;
15059 }
15060 AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows);
15061
15062 g.CurrentDpiScale = 0.0f;
15063 g.CurrentViewport = NULL;
15064 g.MouseViewport = NULL;
15065 for (int n = 0; n < g.Viewports.Size; n++)
15066 {
15067 ImGuiViewportP* viewport = g.Viewports[n];
15068 viewport->Idx = n;
15069
15070 // Erase unused viewports
15071 if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2)
15072 {
15073 DestroyViewport(viewport);
15074 n--;
15075 continue;
15076 }
15077
15078 const bool platform_funcs_available = viewport->PlatformWindowCreated;
15079 if (viewports_enabled)
15080 {
15081 // Update Position and Size (from Platform Window to ImGui) if requested.
15082 // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities.
15083 if (!(viewport->Flags & ImGuiViewportFlags_IsMinimized) && platform_funcs_available)
15084 {
15085 // Viewport->WorkPos and WorkSize will be updated below
15086 if (viewport->PlatformRequestMove)
15087 viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport);
15088 if (viewport->PlatformRequestResize)
15089 viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport);
15090 }
15091 }
15092
15093 // Update/copy monitor info
15094 UpdateViewportPlatformMonitor(viewport);
15095
15096 // Lock down space taken by menu bars and status bars, reset the offset for functions like BeginMainMenuBar() to alter them again.
15097 viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
15098 viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
15099 viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);
15100 viewport->UpdateWorkRect();
15101
15102 // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back.
15103 viewport->Alpha = 1.0f;
15104
15105 // Translate Dear ImGui windows when a Host Viewport has been moved
15106 // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!)
15107 const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos;
15108 if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f))
15109 TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos);
15110
15111 // Update DPI scale
15112 float new_dpi_scale;
15113 if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available)
15114 new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport);
15115 else if (viewport->PlatformMonitor != -1)
15116 new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
15117 else
15118 new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f;
15119 if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale)
15120 {
15121 float scale_factor = new_dpi_scale / viewport->DpiScale;
15122 if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
15123 ScaleWindowsInViewport(viewport, scale_factor);
15124 //if (viewport == GetMainViewport())
15125 // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor);
15126
15127 // Scale our window moving pivot so that the window will rescale roughly around the mouse position.
15128 // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border.
15129 // (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.)
15130 //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport)
15131 // g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor);
15132 }
15133 viewport->DpiScale = new_dpi_scale;
15134 }
15135
15136 // Update fallback monitor
15137 g.PlatformMonitorsFullWorkRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
15138 if (g.PlatformIO.Monitors.Size == 0)
15139 {
15140 ImGuiPlatformMonitor* monitor = &g.FallbackMonitor;
15141 monitor->MainPos = main_viewport->Pos;
15142 monitor->MainSize = main_viewport->Size;
15143 monitor->WorkPos = main_viewport->WorkPos;
15144 monitor->WorkSize = main_viewport->WorkSize;
15145 monitor->DpiScale = main_viewport->DpiScale;
15146 g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos);
15147 g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize);
15148 }
15149 for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
15150 {
15151 g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos);
15152 g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos + monitor.WorkSize);
15153 }
15154
15155 if (!viewports_enabled)
15156 {
15157 g.MouseViewport = main_viewport;
15158 return;
15159 }
15160
15161 // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport.
15162 // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set.
15163 ImGuiViewportP* viewport_hovered = NULL;
15164 if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
15165 {
15166 viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
15167 if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
15168 viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback.
15169 }
15170 else
15171 {
15172 // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
15173 // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
15174 // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order.
15175 // 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)
15176 viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
15177 }
15178 if (viewport_hovered != NULL)
15179 g.MouseLastHoveredViewport = viewport_hovered;
15180 else if (g.MouseLastHoveredViewport == NULL)
15181 g.MouseLastHoveredViewport = g.Viewports[0];
15182
15183 // Update mouse reference viewport
15184 // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode)
15185 // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details)
15186 if (g.MovingWindow && g.MovingWindow->Viewport)
15187 g.MouseViewport = g.MovingWindow->Viewport;
15188 else
15189 g.MouseViewport = g.MouseLastHoveredViewport;
15190
15191 // When dragging something, always refer to the last hovered viewport.
15192 // - when releasing a moving window we will revert to aiming behind (at viewport_hovered)
15193 // - 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)
15194 // - 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.
15195 // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that.
15196 const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;
15197 if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL)
15198 viewport_hovered = g.MouseLastHoveredViewport;
15199 if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown())
15200 if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
15201 g.MouseViewport = viewport_hovered;
15202
15203 IM_ASSERT(g.MouseViewport != NULL);
15204}
15205
15206// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some)
15207static void ImGui::UpdateViewportsEndFrame()
15208{
15209 ImGuiContext& g = *GImGui;
15210 g.PlatformIO.Viewports.resize(0);
15211 for (int i = 0; i < g.Viewports.Size; i++)
15212 {
15213 ImGuiViewportP* viewport = g.Viewports[i];
15214 viewport->LastPos = viewport->Pos;
15215 if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f)
15216 if (i > 0) // Always include main viewport in the list
15217 continue;
15218 if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window))
15219 continue;
15220 if (i > 0)
15221 IM_ASSERT(viewport->Window != NULL);
15222 g.PlatformIO.Viewports.push_back(viewport);
15223 }
15224 g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called
15225}
15226
15227// FIXME: We should ideally refactor the system to call this every frame (we currently don't)
15228ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags)
15229{
15230 ImGuiContext& g = *GImGui;
15231 IM_ASSERT(id != 0);
15232
15233 flags |= ImGuiViewportFlags_IsPlatformWindow;
15234 if (window != NULL)
15235 {
15236 if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window)
15237 flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing;
15238 if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs))
15239 flags |= ImGuiViewportFlags_NoInputs;
15240 if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)
15241 flags |= ImGuiViewportFlags_NoFocusOnAppearing;
15242 }
15243
15244 ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id);
15245 if (viewport)
15246 {
15247 // Always update for main viewport as we are already pulling correct platform pos/size (see #4900)
15248 if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
15249 viewport->Pos = pos;
15250 if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID)
15251 viewport->Size = size;
15252 viewport->Flags = flags | (viewport->Flags & (ImGuiViewportFlags_IsMinimized | ImGuiViewportFlags_IsFocused)); // Preserve existing flags
15253 }
15254 else
15255 {
15256 // New viewport
15257 viewport = IM_NEW(ImGuiViewportP)();
15258 viewport->ID = id;
15259 viewport->Idx = g.Viewports.Size;
15260 viewport->Pos = viewport->LastPos = pos;
15261 viewport->Size = size;
15262 viewport->Flags = flags;
15263 UpdateViewportPlatformMonitor(viewport);
15264 g.Viewports.push_back(viewport);
15265 g.ViewportCreatedCount++;
15266 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'\n", id, window ? window->Name : "<NULL>");
15267
15268 // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport.
15269 // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame
15270 g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x);
15271 g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y);
15272 g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x);
15273 g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y);
15274
15275 // Store initial DpiScale before the OS platform window creation, based on expected monitor data.
15276 // This is so we can select an appropriate font size on the first frame of our window lifetime
15277 if (viewport->PlatformMonitor != -1)
15278 viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale;
15279 }
15280
15281 viewport->Window = window;
15282 viewport->LastFrameActive = g.FrameCount;
15283 viewport->UpdateWorkRect();
15284 IM_ASSERT(window == NULL || viewport->ID == window->ID);
15285
15286 if (window != NULL)
15287 window->ViewportOwned = true;
15288
15289 return viewport;
15290}
15291
15292static void ImGui::DestroyViewport(ImGuiViewportP* viewport)
15293{
15294 // Clear references to this viewport in windows (window->ViewportId becomes the master data)
15295 ImGuiContext& g = *GImGui;
15296 for (ImGuiWindow* window : g.Windows)
15297 {
15298 if (window->Viewport != viewport)
15299 continue;
15300 window->Viewport = NULL;
15301 window->ViewportOwned = false;
15302 }
15303 if (viewport == g.MouseLastHoveredViewport)
15304 g.MouseLastHoveredViewport = NULL;
15305
15306 // Destroy
15307 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Delete Viewport %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
15308 DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here.
15309 IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false);
15310 IM_ASSERT(g.Viewports[viewport->Idx] == viewport);
15311 g.Viewports.erase(g.Viewports.Data + viewport->Idx);
15312 IM_DELETE(viewport);
15313}
15314
15315// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten.
15316static void ImGui::WindowSelectViewport(ImGuiWindow* window)
15317{
15318 ImGuiContext& g = *GImGui;
15319 ImGuiWindowFlags flags = window->Flags;
15320 window->ViewportAllowPlatformMonitorExtend = -1;
15321
15322 // Restore main viewport if multi-viewport is not supported by the backend
15323 ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport();
15324 if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
15325 {
15326 SetWindowViewport(window, main_viewport);
15327 return;
15328 }
15329 window->ViewportOwned = false;
15330
15331 // Appearing popups reset their viewport so they can inherit again
15332 if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing)
15333 {
15334 window->Viewport = NULL;
15335 window->ViewportId = 0;
15336 }
15337
15338 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0)
15339 {
15340 // By default inherit from parent window
15341 if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive))
15342 window->Viewport = window->ParentWindow->Viewport;
15343
15344 // 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
15345 if (window->Viewport == NULL && window->ViewportId != 0)
15346 {
15347 window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId);
15348 if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX)
15349 window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None);
15350 }
15351 }
15352
15353 bool lock_viewport = false;
15354 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport)
15355 {
15356 // Code explicitly request a viewport
15357 window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId);
15358 window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet.
15359 if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL)
15360 {
15361 window->Viewport->Window = window;
15362 window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node)
15363 }
15364 lock_viewport = true;
15365 }
15366 else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu))
15367 {
15368 // Always inherit viewport from parent window
15369 if (window->DockNode && window->DockNode->HostWindow)
15370 IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport);
15371 window->Viewport = window->ParentWindow->Viewport;
15372 }
15373 else if (window->DockNode && window->DockNode->HostWindow)
15374 {
15375 // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame
15376 window->Viewport = window->DockNode->HostWindow->Viewport;
15377 }
15378 else if (flags & ImGuiWindowFlags_Tooltip)
15379 {
15380 window->Viewport = g.MouseViewport;
15381 }
15382 else if (GetWindowAlwaysWantOwnViewport(window))
15383 {
15384 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
15385 }
15386 else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid())
15387 {
15388 if (window->Viewport != NULL && window->Viewport->Window == window)
15389 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
15390 }
15391 else
15392 {
15393 // Merge into host viewport?
15394 // We cannot test window->ViewportOwned as it set lower in the function.
15395 // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212)
15396 bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap));
15397 if (try_to_merge_into_host_viewport)
15398 UpdateTryMergeWindowIntoHostViewports(window);
15399 }
15400
15401 // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport
15402 if (window->Viewport == NULL)
15403 if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport))
15404 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None);
15405
15406 // Mark window as allowed to protrude outside of its viewport and into the current monitor
15407 if (!lock_viewport)
15408 {
15409 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
15410 {
15411 // We need to take account of the possibility that mouse may become invalid.
15412 // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds.
15413 ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos;
15414 bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow);
15415 bool mouse_valid = IsMousePosValid(&mouse_ref);
15416 if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid))
15417 window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos());
15418 else
15419 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
15420 }
15421 else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL)
15422 {
15423 // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code.
15424 const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true;
15425 if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible)
15426 {
15427 // Steal/transfer ownership
15428 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name);
15429 window->Viewport->Window = window;
15430 window->Viewport->ID = window->ID;
15431 window->Viewport->LastNameHash = 0;
15432 }
15433 else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge?
15434 {
15435 // New viewport
15436 window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing);
15437 }
15438 }
15439 else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0)
15440 {
15441 // Regular (non-child, non-popup) windows by default are also allowed to protrude
15442 // Child windows are kept contained within their parent.
15443 window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor;
15444 }
15445 }
15446
15447 // Update flags
15448 window->ViewportOwned = (window == window->Viewport->Window);
15449 window->ViewportId = window->Viewport->ID;
15450
15451 // If the OS window has a title bar, hide our imgui title bar
15452 //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration))
15453 // window->Flags |= ImGuiWindowFlags_NoTitleBar;
15454}
15455
15456void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack)
15457{
15458 ImGuiContext& g = *GImGui;
15459
15460 bool viewport_rect_changed = false;
15461
15462 // Synchronize window --> viewport in most situations
15463 // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM
15464 if (window->Viewport->PlatformRequestMove)
15465 {
15466 window->Pos = window->Viewport->Pos;
15467 MarkIniSettingsDirty(window);
15468 }
15469 else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0)
15470 {
15471 viewport_rect_changed = true;
15472 window->Viewport->Pos = window->Pos;
15473 }
15474
15475 if (window->Viewport->PlatformRequestResize)
15476 {
15477 window->Size = window->SizeFull = window->Viewport->Size;
15478 MarkIniSettingsDirty(window);
15479 }
15480 else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0)
15481 {
15482 viewport_rect_changed = true;
15483 window->Viewport->Size = window->Size;
15484 }
15485 window->Viewport->UpdateWorkRect();
15486
15487 // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame()
15488 // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect.
15489 if (viewport_rect_changed)
15490 UpdateViewportPlatformMonitor(window->Viewport);
15491
15492 // Update common viewport flags
15493 const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear;
15494 ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear;
15495 ImGuiWindowFlags window_flags = window->Flags;
15496 const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0;
15497 const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0;
15498 if (window_flags & ImGuiWindowFlags_Tooltip)
15499 viewport_flags |= ImGuiViewportFlags_TopMost;
15500 if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal)
15501 viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon;
15502 if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window)
15503 viewport_flags |= ImGuiViewportFlags_NoDecoration;
15504
15505 // Not correct to set modal as topmost because:
15506 // - Because other popups can be stacked above a modal (e.g. combo box in a modal)
15507 // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost"
15508 //if (flags & ImGuiWindowFlags_Modal)
15509 // viewport_flags |= ImGuiViewportFlags_TopMost;
15510
15511 // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them
15512 // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration).
15513 // 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,
15514 // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere.
15515 if (is_short_lived_floating_window && !is_modal)
15516 viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick;
15517
15518 // We can overwrite viewport flags using ImGuiWindowClass (advanced users)
15519 if (window->WindowClass.ViewportFlagsOverrideSet)
15520 viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet;
15521 if (window->WindowClass.ViewportFlagsOverrideClear)
15522 viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear;
15523
15524 // We can also tell the backend that clearing the platform window won't be necessary,
15525 // as our window background is filling the viewport and we have disabled BgAlpha.
15526 // FIXME: Work on support for per-viewport transparency (#2766)
15527 if (!(window_flags & ImGuiWindowFlags_NoBackground))
15528 viewport_flags |= ImGuiViewportFlags_NoRendererClear;
15529
15530 window->Viewport->Flags = viewport_flags;
15531
15532 // Update parent viewport ID
15533 // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport())
15534 if (window->WindowClass.ParentViewportId != (ImGuiID)-1)
15535 window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId;
15536 else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive))
15537 window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID;
15538 else
15539 window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID;
15540}
15541
15542// Called by user at the end of the main loop, after EndFrame()
15543// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api.
15544void ImGui::UpdatePlatformWindows()
15545{
15546 ImGuiContext& g = *GImGui;
15547 IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?");
15548 IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount);
15549 g.FrameCountPlatformEnded = g.FrameCount;
15550 if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable))
15551 return;
15552
15553 // Create/resize/destroy platform windows to match each active viewport.
15554 // Skip the main viewport (index 0), which is always fully handled by the application!
15555 for (int i = 1; i < g.Viewports.Size; i++)
15556 {
15557 ImGuiViewportP* viewport = g.Viewports[i];
15558
15559 // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window
15560 // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame)
15561 bool destroy_platform_window = false;
15562 destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1);
15563 destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window));
15564 if (destroy_platform_window)
15565 {
15566 DestroyPlatformWindow(viewport);
15567 continue;
15568 }
15569
15570 // New windows that appears directly in a new viewport won't always have a size on their first frame
15571 if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0)
15572 continue;
15573
15574 // Create window
15575 const bool is_new_platform_window = (viewport->PlatformWindowCreated == false);
15576 if (is_new_platform_window)
15577 {
15578 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Create Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
15579 g.PlatformIO.Platform_CreateWindow(viewport);
15580 if (g.PlatformIO.Renderer_CreateWindow != NULL)
15581 g.PlatformIO.Renderer_CreateWindow(viewport);
15582 g.PlatformWindowsCreatedCount++;
15583 viewport->LastNameHash = 0;
15584 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?)
15585 viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it.
15586 viewport->PlatformWindowCreated = true;
15587 }
15588
15589 // Apply Position and Size (from ImGui to Platform/Renderer backends)
15590 if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove)
15591 g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
15592 if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize)
15593 g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size);
15594 if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize)
15595 g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size);
15596 viewport->LastPlatformPos = viewport->Pos;
15597 viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size;
15598
15599 // Update title bar (if it changed)
15600 if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window))
15601 {
15602 const char* title_begin = window_for_title->Name;
15603 char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin);
15604 const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin);
15605 if (viewport->LastNameHash != title_hash)
15606 {
15607 char title_end_backup_c = *title_end;
15608 *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain.
15609 g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin);
15610 *title_end = title_end_backup_c;
15611 viewport->LastNameHash = title_hash;
15612 }
15613 }
15614
15615 // Update alpha (if it changed)
15616 if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha)
15617 g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha);
15618 viewport->LastAlpha = viewport->Alpha;
15619
15620 // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed.
15621 if (g.PlatformIO.Platform_UpdateWindow)
15622 g.PlatformIO.Platform_UpdateWindow(viewport);
15623
15624 if (is_new_platform_window)
15625 {
15626 // 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)
15627 if (g.FrameCount < 3)
15628 viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing;
15629
15630 // Show window
15631 g.PlatformIO.Platform_ShowWindow(viewport);
15632
15633 // Even without focus, we assume the window becomes front-most.
15634 // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available.
15635 if (viewport->LastFocusedStampCount != g.ViewportFocusedStampCount)
15636 viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount;
15637 }
15638
15639 // Clear request flags
15640 viewport->ClearRequestFlags();
15641 }
15642}
15643
15644// This is a default/basic function for performing the rendering/swap of multiple Platform Windows.
15645// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves.
15646// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself:
15647//
15648// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
15649// for (int i = 1; i < platform_io.Viewports.Size; i++)
15650// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
15651// MyRenderFunction(platform_io.Viewports[i], my_args);
15652// for (int i = 1; i < platform_io.Viewports.Size; i++)
15653// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0)
15654// MySwapBufferFunction(platform_io.Viewports[i], my_args);
15655//
15656void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg)
15657{
15658 // Skip the main viewport (index 0), which is always fully handled by the application!
15659 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
15660 for (int i = 1; i < platform_io.Viewports.Size; i++)
15661 {
15662 ImGuiViewport* viewport = platform_io.Viewports[i];
15663 if (viewport->Flags & ImGuiViewportFlags_IsMinimized)
15664 continue;
15665 if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg);
15666 if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg);
15667 }
15668 for (int i = 1; i < platform_io.Viewports.Size; i++)
15669 {
15670 ImGuiViewport* viewport = platform_io.Viewports[i];
15671 if (viewport->Flags & ImGuiViewportFlags_IsMinimized)
15672 continue;
15673 if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg);
15674 if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg);
15675 }
15676}
15677
15678static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos)
15679{
15680 ImGuiContext& g = *GImGui;
15681 for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++)
15682 {
15683 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
15684 if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos))
15685 return monitor_n;
15686 }
15687 return -1;
15688}
15689
15690// Search for the monitor with the largest intersection area with the given rectangle
15691// We generally try to avoid searching loops but the monitor count should be very small here
15692// FIXME-OPT: We could test the last monitor used for that viewport first, and early
15693static int ImGui::FindPlatformMonitorForRect(const ImRect& rect)
15694{
15695 ImGuiContext& g = *GImGui;
15696
15697 const int monitor_count = g.PlatformIO.Monitors.Size;
15698 if (monitor_count <= 1)
15699 return monitor_count - 1;
15700
15701 // 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.
15702 // This is necessary for tooltips which always resize down to zero at first.
15703 const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f);
15704 int best_monitor_n = -1;
15705 float best_monitor_surface = 0.001f;
15706
15707 for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++)
15708 {
15709 const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n];
15710 const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize);
15711 if (monitor_rect.Contains(rect))
15712 return monitor_n;
15713 ImRect overlapping_rect = rect;
15714 overlapping_rect.ClipWithFull(monitor_rect);
15715 float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight();
15716 if (overlapping_surface < best_monitor_surface)
15717 continue;
15718 best_monitor_surface = overlapping_surface;
15719 best_monitor_n = monitor_n;
15720 }
15721 return best_monitor_n;
15722}
15723
15724// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor)
15725static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport)
15726{
15727 viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect());
15728}
15729
15730// Return value is always != NULL, but don't hold on it across frames.
15731const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p)
15732{
15733 ImGuiContext& g = *GImGui;
15734 ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p;
15735 int monitor_idx = viewport->PlatformMonitor;
15736 if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size)
15737 return &g.PlatformIO.Monitors[monitor_idx];
15738 return &g.FallbackMonitor;
15739}
15740
15741void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport)
15742{
15743 ImGuiContext& g = *GImGui;
15744 if (viewport->PlatformWindowCreated)
15745 {
15746 IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Destroy Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a");
15747 if (g.PlatformIO.Renderer_DestroyWindow)
15748 g.PlatformIO.Renderer_DestroyWindow(viewport);
15749 if (g.PlatformIO.Platform_DestroyWindow)
15750 g.PlatformIO.Platform_DestroyWindow(viewport);
15751 IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL);
15752
15753 // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize()
15754 // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public.
15755 if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID)
15756 viewport->PlatformWindowCreated = false;
15757 }
15758 else
15759 {
15760 IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL);
15761 }
15762 viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL;
15763 viewport->ClearRequestFlags();
15764}
15765
15766void ImGui::DestroyPlatformWindows()
15767{
15768 // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend
15769 // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData.
15770 // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling
15771 // code to operator a consistent manner.
15772 // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without
15773 // crashing if it doesn't have data stored.
15774 ImGuiContext& g = *GImGui;
15775 for (ImGuiViewportP* viewport : g.Viewports)
15776 DestroyPlatformWindow(viewport);
15777}
15778
15779
15780//-----------------------------------------------------------------------------
15781// [SECTION] DOCKING
15782//-----------------------------------------------------------------------------
15783// Docking: Internal Types
15784// Docking: Forward Declarations
15785// Docking: ImGuiDockContext
15786// Docking: ImGuiDockContext Docking/Undocking functions
15787// Docking: ImGuiDockNode
15788// Docking: ImGuiDockNode Tree manipulation functions
15789// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
15790// Docking: Builder Functions
15791// Docking: Begin/End Support Functions (called from Begin/End)
15792// Docking: Settings
15793//-----------------------------------------------------------------------------
15794
15795//-----------------------------------------------------------------------------
15796// Typical Docking call flow: (root level is generally public API):
15797//-----------------------------------------------------------------------------
15798// - NewFrame() new dear imgui frame
15799// | DockContextNewFrameUpdateUndocking() - process queued undocking requests
15800// | - DockContextProcessUndockWindow() - process one window undocking request
15801// | - DockContextProcessUndockNode() - process one whole node undocking request
15802// | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes
15803// | - update g.HoveredDockNode - [debug] update node hovered by mouse
15804// | - DockContextProcessDock() - process one docking request
15805// | - DockNodeUpdate()
15806// | - DockNodeUpdateForRootNode()
15807// | - DockNodeUpdateFlagsAndCollapse()
15808// | - DockNodeFindInfo()
15809// | - destroy unused node or tab bar
15810// | - create dock node host window
15811// | - Begin() etc.
15812// | - DockNodeStartMouseMovingWindow()
15813// | - DockNodeTreeUpdatePosSize()
15814// | - DockNodeTreeUpdateSplitter()
15815// | - draw node background
15816// | - DockNodeUpdateTabBar() - create/update tab bar for a docking node
15817// | - DockNodeAddTabBar()
15818// | - DockNodeWindowMenuUpdate()
15819// | - DockNodeCalcTabBarLayout()
15820// | - BeginTabBarEx()
15821// | - TabItemEx() calls
15822// | - EndTabBar()
15823// | - BeginDockableDragDropTarget()
15824// | - DockNodeUpdate() - recurse into child nodes...
15825//-----------------------------------------------------------------------------
15826// - DockSpace() user submit a dockspace into a window
15827// | Begin(Child) - create a child window
15828// | DockNodeUpdate() - call main dock node update function
15829// | End(Child)
15830// | ItemSize()
15831//-----------------------------------------------------------------------------
15832// - Begin()
15833// | BeginDocked()
15834// | BeginDockableDragDropSource()
15835// | BeginDockableDragDropTarget()
15836// | - DockNodePreviewDockRender()
15837//-----------------------------------------------------------------------------
15838// - EndFrame()
15839// | DockContextEndFrame()
15840//-----------------------------------------------------------------------------
15841
15842//-----------------------------------------------------------------------------
15843// Docking: Internal Types
15844//-----------------------------------------------------------------------------
15845// - ImGuiDockRequestType
15846// - ImGuiDockRequest
15847// - ImGuiDockPreviewData
15848// - ImGuiDockNodeSettings
15849// - ImGuiDockContext
15850//-----------------------------------------------------------------------------
15851
15859
15861{
15863 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)
15864 ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into
15865 ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
15870 ImGuiDockNode* UndockTargetNode;
15871
15873 {
15877 DockSplitDir = ImGuiDir_None;
15878 DockSplitRatio = 0.5f;
15879 DockSplitOuter = false;
15880 }
15881};
15882
15884{
15885 ImGuiDockNode FutureNode;
15888 bool IsSidesAvailable; // Hold your breath, grammar freaks..
15889 bool IsSplitDirExplicit; // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window)
15890 ImGuiDockNode* SplitNode;
15891 ImGuiDir SplitDir;
15893 ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects()
15894
15895 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); }
15896};
15897
15898// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes)
15900{
15901 ImGuiID ID;
15905 signed char SplitAxis;
15906 char Depth;
15907 ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_)
15908 ImVec2ih Pos;
15909 ImVec2ih Size;
15910 ImVec2ih SizeRef;
15911 ImGuiDockNodeSettings() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; }
15912};
15913
15914//-----------------------------------------------------------------------------
15915// Docking: Forward Declarations
15916//-----------------------------------------------------------------------------
15917
15918namespace ImGui
15919{
15920 // ImGuiDockContext
15921 static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
15922 static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
15923 static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
15924 static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
15925 static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx);
15926 static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window);
15927 static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
15928 static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all
15929
15930 // ImGuiDockNode
15931 static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar);
15932 static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
15933 static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node);
15934 static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id);
15935 static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node);
15936 static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id);
15937 static void DockNodeHideHostWindow(ImGuiDockNode* node);
15938 static void DockNodeUpdate(ImGuiDockNode* node);
15939 static void DockNodeUpdateForRootNode(ImGuiDockNode* node);
15940 static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node);
15941 static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node);
15942 static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window);
15943 static void DockNodeAddTabBar(ImGuiDockNode* node);
15944 static void DockNodeRemoveTabBar(ImGuiDockNode* node);
15945 static void DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar);
15946 static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node);
15947 static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window);
15948 static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window);
15949 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);
15950 static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data);
15951 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);
15952 static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired);
15953 static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos);
15954 static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; }
15955 static int DockNodeGetTabOrder(ImGuiWindow* window);
15956
15957 // ImGuiDockNode tree manipulations
15958 static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node);
15959 static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child);
15960 static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL);
15961 static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node);
15962 static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos);
15963 static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node);
15964
15965 // Settings
15966 static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id);
15967 static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count);
15968 static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id);
15969 static void DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
15970 static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
15971 static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
15972 static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
15973 static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
15974}
15975
15976//-----------------------------------------------------------------------------
15977// Docking: ImGuiDockContext
15978//-----------------------------------------------------------------------------
15979// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
15980// 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.
15981// At boot time only, we run a simple GC to remove nodes that have no references.
15982// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
15983// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
15984// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data.
15985//-----------------------------------------------------------------------------
15986// - DockContextInitialize()
15987// - DockContextShutdown()
15988// - DockContextClearNodes()
15989// - DockContextRebuildNodes()
15990// - DockContextNewFrameUpdateUndocking()
15991// - DockContextNewFrameUpdateDocking()
15992// - DockContextEndFrame()
15993// - DockContextFindNodeByID()
15994// - DockContextBindNodeToWindow()
15995// - DockContextGenNodeID()
15996// - DockContextAddNode()
15997// - DockContextRemoveNode()
15998// - ImGuiDockContextPruneNodeData
15999// - DockContextPruneUnusedSettingsNodes()
16000// - DockContextBuildNodesFromSettings()
16001// - DockContextBuildAddWindowsToNodes()
16002//-----------------------------------------------------------------------------
16003
16004void ImGui::DockContextInitialize(ImGuiContext* ctx)
16005{
16006 ImGuiContext& g = *ctx;
16007
16008 // Add .ini handle for persistent docking data
16009 ImGuiSettingsHandler ini_handler;
16010 ini_handler.TypeName = "Docking";
16011 ini_handler.TypeHash = ImHashStr("Docking");
16012 ini_handler.ClearAllFn = DockSettingsHandler_ClearAll;
16013 ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read
16014 ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen;
16015 ini_handler.ReadLineFn = DockSettingsHandler_ReadLine;
16016 ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll;
16017 ini_handler.WriteAllFn = DockSettingsHandler_WriteAll;
16018 g.SettingsHandlers.push_back(ini_handler);
16019
16020 g.DockNodeWindowMenuHandler = &DockNodeWindowMenuHandler_Default;
16021}
16022
16023void ImGui::DockContextShutdown(ImGuiContext* ctx)
16024{
16025 ImGuiDockContext* dc = &ctx->DockContext;
16026 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16027 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16028 IM_DELETE(node);
16029}
16030
16031void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs)
16032{
16033 IM_UNUSED(ctx);
16034 IM_ASSERT(ctx == GImGui);
16035 DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs);
16036 DockBuilderRemoveNodeChildNodes(root_id);
16037}
16038
16039// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
16040// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!)
16041void ImGui::DockContextRebuildNodes(ImGuiContext* ctx)
16042{
16043 ImGuiContext& g = *ctx;
16044 ImGuiDockContext* dc = &ctx->DockContext;
16045 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRebuildNodes\n");
16046 SaveIniSettingsToMemory();
16047 ImGuiID root_id = 0; // Rebuild all
16048 DockContextClearNodes(ctx, root_id, false);
16049 DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
16050 DockContextBuildAddWindowsToNodes(ctx, root_id);
16051}
16052
16053// Docking context update function, called by NewFrame()
16054void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx)
16055{
16056 ImGuiContext& g = *ctx;
16057 ImGuiDockContext* dc = &ctx->DockContext;
16058 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
16059 {
16060 if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0)
16061 DockContextClearNodes(ctx, 0, true);
16062 return;
16063 }
16064
16065 // Setting NoSplit at runtime merges all nodes
16066 if (g.IO.ConfigDockingNoSplit)
16067 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16068 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16069 if (node->IsRootNode() && node->IsSplitNode())
16070 {
16071 DockBuilderRemoveNodeChildNodes(node->ID);
16072 //dc->WantFullRebuild = true;
16073 }
16074
16075 // Process full rebuild
16076#if 0
16077 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
16078 dc->WantFullRebuild = true;
16079#endif
16080 if (dc->WantFullRebuild)
16081 {
16082 DockContextRebuildNodes(ctx);
16083 dc->WantFullRebuild = false;
16084 }
16085
16086 // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame)
16087 for (ImGuiDockRequest& req : dc->Requests)
16088 {
16089 if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetWindow)
16090 DockContextProcessUndockWindow(ctx, req.UndockTargetWindow);
16091 else if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetNode)
16092 DockContextProcessUndockNode(ctx, req.UndockTargetNode);
16093 }
16094}
16095
16096// Docking context update function, called by NewFrame()
16097void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx)
16098{
16099 ImGuiContext& g = *ctx;
16100 ImGuiDockContext* dc = &ctx->DockContext;
16101 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
16102 return;
16103
16104 // [DEBUG] Store hovered dock node.
16105 // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut.
16106 // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering.
16107 g.DebugHoveredDockNode = NULL;
16108 if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow)
16109 {
16110 if (hovered_window->DockNodeAsHost)
16111 g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos);
16112 else if (hovered_window->RootWindow->DockNode)
16113 g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode;
16114 }
16115
16116 // Process Docking requests
16117 for (ImGuiDockRequest& req : dc->Requests)
16118 if (req.Type == ImGuiDockRequestType_Dock)
16119 DockContextProcessDock(ctx, &req);
16120 dc->Requests.resize(0);
16121
16122 // Create windows for each automatic docking nodes
16123 // 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)
16124 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16125 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16126 if (node->IsFloatingNode())
16127 DockNodeUpdate(node);
16128}
16129
16130void ImGui::DockContextEndFrame(ImGuiContext* ctx)
16131{
16132 // Draw backgrounds of node missing their window
16133 ImGuiContext& g = *ctx;
16134 ImGuiDockContext* dc = &g.DockContext;
16135 for (int n = 0; n < dc->Nodes.Data.Size; n++)
16136 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
16137 if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame)
16138 {
16139 ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size);
16140 ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize);
16141 node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
16142 node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags);
16143 }
16144}
16145
16146ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id)
16147{
16148 return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id);
16149}
16150
16151ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx)
16152{
16153 // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used)
16154 // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0
16155 // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted.
16156 ImGuiID id = 0x0001;
16157 while (DockContextFindNodeByID(ctx, id) != NULL)
16158 id++;
16159 return id;
16160}
16161
16162static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
16163{
16164 // 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.
16165 ImGuiContext& g = *ctx;
16166 if (id == 0)
16167 id = DockContextGenNodeID(ctx);
16168 else
16169 IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL);
16170
16171 // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings!
16172 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextAddNode 0x%08X\n", id);
16173 ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id);
16174 ctx->DockContext.Nodes.SetVoidPtr(node->ID, node);
16175 return node;
16176}
16177
16178static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
16179{
16180 ImGuiContext& g = *ctx;
16181 ImGuiDockContext* dc = &ctx->DockContext;
16182
16183 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRemoveNode 0x%08X\n", node->ID);
16184 IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
16185 IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
16186 IM_ASSERT(node->Windows.Size == 0);
16187
16188 if (node->HostWindow)
16189 node->HostWindow->DockNodeAsHost = NULL;
16190
16191 ImGuiDockNode* parent_node = node->ParentNode;
16192 const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
16193 if (merge)
16194 {
16195 IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
16196 ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
16197 DockNodeTreeMerge(&g, parent_node, sibling_node);
16198 }
16199 else
16200 {
16201 for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
16202 if (parent_node->ChildNodes[n] == node)
16203 node->ParentNode->ChildNodes[n] = NULL;
16204 dc->Nodes.SetVoidPtr(node->ID, NULL);
16205 IM_DELETE(node);
16206 }
16207}
16208
16209static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
16210{
16211 const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
16212 const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
16213 return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
16214}
16215
16216// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here.
16223
16224// Garbage collect unused nodes (run once at init time)
16225static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx)
16226{
16227 ImGuiContext& g = *ctx;
16228 ImGuiDockContext* dc = &ctx->DockContext;
16229 IM_ASSERT(g.Windows.Size == 0);
16230
16231 ImPool<ImGuiDockContextPruneNodeData> pool;
16232 pool.Reserve(dc->NodesSettings.Size);
16233
16234 // Count child nodes and compute RootID
16235 for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
16236 {
16237 ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
16238 ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0;
16239 pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID;
16240 if (settings->ParentNodeId)
16241 pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++;
16242 }
16243
16244 // Count reference to dock ids from dockspaces
16245 // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes()
16246 for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
16247 {
16248 ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
16249 if (settings->ParentWindowId != 0)
16250 if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->ParentWindowId))
16251 if (window_settings->DockId)
16252 if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId))
16253 data->CountChildNodes++;
16254 }
16255
16256 // Count reference to dock ids from window settings
16257 // We guard against the possibility of an invalid .ini file (RootID may point to a missing node)
16258 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16259 if (ImGuiID dock_id = settings->DockId)
16260 if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id))
16261 {
16262 data->CountWindows++;
16263 if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId))
16264 data_root->CountChildWindows++;
16265 }
16266
16267 // Prune
16268 for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++)
16269 {
16270 ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n];
16271 ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID);
16272 if (data->CountWindows > 1)
16273 continue;
16274 ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId);
16275
16276 bool remove = false;
16277 remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window
16278 remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window
16279 remove |= (data_root->CountChildWindows == 0);
16280 if (remove)
16281 {
16282 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID);
16283 DockSettingsRemoveNodeReferences(&settings->ID, 1);
16284 settings->ID = 0;
16285 }
16286 }
16287}
16288
16289static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
16290{
16291 // Build nodes
16292 for (int node_n = 0; node_n < node_settings_count; node_n++)
16293 {
16294 ImGuiDockNodeSettings* settings = &node_settings_array[node_n];
16295 if (settings->ID == 0)
16296 continue;
16297 ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID);
16298 node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL;
16299 node->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
16300 node->Size = ImVec2(settings->Size.x, settings->Size.y);
16301 node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y);
16302 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode;
16303 if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL)
16304 node->ParentNode->ChildNodes[0] = node;
16305 else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL)
16306 node->ParentNode->ChildNodes[1] = node;
16307 node->SelectedTabId = settings->SelectedTabId;
16308 node->SplitAxis = (ImGuiAxis)settings->SplitAxis;
16309 node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_);
16310
16311 // Bind host window immediately if it already exist (in case of a rebuild)
16312 // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set.
16313 char host_window_title[20];
16314 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
16315 node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title)));
16316 }
16317}
16318
16319void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id)
16320{
16321 // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
16322 ImGuiContext& g = *ctx;
16323 for (ImGuiWindow* window : g.Windows)
16324 {
16325 if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
16326 continue;
16327 if (window->DockNode != NULL)
16328 continue;
16329
16330 ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
16331 IM_ASSERT(node != NULL); // This should have been called after DockContextBuildNodesFromSettings()
16332 if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id)
16333 DockNodeAddWindow(node, window, true);
16334 }
16335}
16336
16337//-----------------------------------------------------------------------------
16338// Docking: ImGuiDockContext Docking/Undocking functions
16339//-----------------------------------------------------------------------------
16340// - DockContextQueueDock()
16341// - DockContextQueueUndockWindow()
16342// - DockContextQueueUndockNode()
16343// - DockContextQueueNotifyRemovedNode()
16344// - DockContextProcessDock()
16345// - DockContextProcessUndockWindow()
16346// - DockContextProcessUndockNode()
16347// - DockContextCalcDropPosForDocking()
16348//-----------------------------------------------------------------------------
16349
16350void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer)
16351{
16352 IM_ASSERT(target != payload);
16353 ImGuiDockRequest req;
16355 req.DockTargetWindow = target;
16356 req.DockTargetNode = target_node;
16357 req.DockPayload = payload;
16358 req.DockSplitDir = split_dir;
16359 req.DockSplitRatio = split_ratio;
16360 req.DockSplitOuter = split_outer;
16361 ctx->DockContext.Requests.push_back(req);
16362}
16363
16364void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window)
16365{
16366 ImGuiDockRequest req;
16368 req.UndockTargetWindow = window;
16369 ctx->DockContext.Requests.push_back(req);
16370}
16371
16372void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
16373{
16374 ImGuiDockRequest req;
16376 req.UndockTargetNode = node;
16377 ctx->DockContext.Requests.push_back(req);
16378}
16379
16380void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
16381{
16382 ImGuiDockContext* dc = &ctx->DockContext;
16383 for (ImGuiDockRequest& req : dc->Requests)
16384 if (req.DockTargetNode == node)
16385 req.Type = ImGuiDockRequestType_None;
16386}
16387
16388void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
16389{
16390 IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
16391 IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
16392
16393 ImGuiContext& g = *ctx;
16394 IM_UNUSED(g);
16395
16396 ImGuiWindow* payload_window = req->DockPayload; // Optional
16397 ImGuiWindow* target_window = req->DockTargetWindow;
16398 ImGuiDockNode* node = req->DockTargetNode;
16399 if (payload_window)
16400 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);
16401 else
16402 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir);
16403
16404 // Decide which Tab will be selected at the end of the operation
16405 ImGuiID next_selected_id = 0;
16406 ImGuiDockNode* payload_node = NULL;
16407 if (payload_window)
16408 {
16409 payload_node = payload_window->DockNodeAsHost;
16410 payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later.
16411 if (payload_node && payload_node->IsLeafNode())
16412 next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
16413 if (payload_node == NULL)
16414 next_selected_id = payload_window->TabId;
16415 }
16416
16417 // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
16418 // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==.
16419 if (node)
16420 IM_ASSERT(node->LastFrameAlive <= g.FrameCount);
16421 if (node && target_window && node == target_window->DockNodeAsHost)
16422 IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode());
16423
16424 // Create new node and add existing window to it
16425 if (node == NULL)
16426 {
16427 node = DockContextAddNode(ctx, 0);
16428 node->Pos = target_window->Pos;
16429 node->Size = target_window->Size;
16430 if (target_window->DockNodeAsHost == NULL)
16431 {
16432 DockNodeAddWindow(node, target_window, true);
16433 node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted;
16434 target_window->DockIsActive = true;
16435 }
16436 }
16437
16438 ImGuiDir split_dir = req->DockSplitDir;
16439 if (split_dir != ImGuiDir_None)
16440 {
16441 // Split into two, one side will be our payload node unless we are dropping a loose window
16442 const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
16443 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
16444 const float split_ratio = req->DockSplitRatio;
16445 DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here!
16446 ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1];
16447 new_node->HostWindow = node->HostWindow;
16448 node = new_node;
16449 }
16450 node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
16451
16452 if (node != payload_node)
16453 {
16454 // 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!)
16455 if (node->Windows.Size > 0 && node->TabBar == NULL)
16456 {
16457 DockNodeAddTabBar(node);
16458 for (int n = 0; n < node->Windows.Size; n++)
16459 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
16460 }
16461
16462 if (payload_node != NULL)
16463 {
16464 // Transfer full payload node (with 1+ child windows or child nodes)
16465 if (payload_node->IsSplitNode())
16466 {
16467 if (node->Windows.Size > 0)
16468 {
16469 // 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.
16470 // In this situation, we move the windows of the target node into the currently visible node of the payload.
16471 // This allows us to preserve some of the underlying dock tree settings nicely.
16472 IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted.
16473 ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows;
16474 if (visible_node->TabBar)
16475 IM_ASSERT(visible_node->TabBar->Tabs.Size > 0);
16476 DockNodeMoveWindows(node, visible_node);
16477 DockNodeMoveWindows(visible_node, node);
16478 DockSettingsRenameNodeReferences(node->ID, visible_node->ID);
16479 }
16480 if (node->IsCentralNode())
16481 {
16482 // Central node property needs to be moved to a leaf node, pick the last focused one.
16483 // FIXME-DOCK: If we had to transfer other flags here, what would the policy be?
16484 ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId);
16485 IM_ASSERT(last_focused_node != NULL);
16486 ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node);
16487 IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node));
16488 last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
16489 node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode);
16490 last_focused_root_node->CentralNode = last_focused_node;
16491 }
16492
16493 IM_ASSERT(node->Windows.Size == 0);
16494 DockNodeMoveChildNodes(node, payload_node);
16495 }
16496 else
16497 {
16498 const ImGuiID payload_dock_id = payload_node->ID;
16499 DockNodeMoveWindows(node, payload_node);
16500 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
16501 }
16502 DockContextRemoveNode(ctx, payload_node, true);
16503 }
16504 else if (payload_window)
16505 {
16506 // Transfer single window
16507 const ImGuiID payload_dock_id = payload_window->DockId;
16508 node->VisibleWindow = payload_window;
16509 DockNodeAddWindow(node, payload_window, true);
16510 if (payload_dock_id != 0)
16511 DockSettingsRenameNodeReferences(payload_dock_id, node->ID);
16512 }
16513 }
16514 else
16515 {
16516 // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar
16517 node->WantHiddenTabBarUpdate = true;
16518 }
16519
16520 // Update selection immediately
16521 if (ImGuiTabBar* tab_bar = node->TabBar)
16522 tab_bar->NextSelectedTabId = next_selected_id;
16523 MarkIniSettingsDirty();
16524}
16525
16526// Problem:
16527// Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more
16528// than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic
16529// with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be
16530// due to missing ImGuiBackendFlags_HasMouseCursors backend flag).
16531// Solution:
16532// When undocking a window we currently force its maximum size to 90% of the host viewport or monitor.
16533// Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch).
16534static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport)
16535{
16536 if (ref_viewport == NULL)
16537 return size;
16538
16539 ImGuiContext& g = *GImGui;
16540 ImVec2 max_size = ImTrunc(ref_viewport->WorkSize * 0.90f);
16541 if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)
16542 {
16543 const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport);
16544 max_size = ImTrunc(monitor->WorkSize * 0.90f);
16545 }
16546 return ImMin(size, max_size);
16547}
16548
16549void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref)
16550{
16551 ImGuiContext& g = *ctx;
16552 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref);
16553 if (window->DockNode)
16554 DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId);
16555 else
16556 window->DockId = 0;
16557 window->Collapsed = false;
16558 window->DockIsActive = false;
16559 window->DockNodeIsVisible = window->DockTabIsVisible = false;
16560 window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport);
16561
16562 MarkIniSettingsDirty();
16563}
16564
16565void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node)
16566{
16567 ImGuiContext& g = *ctx;
16568 IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockNode node %08X\n", node->ID);
16569 IM_ASSERT(node->IsLeafNode());
16570 IM_ASSERT(node->Windows.Size >= 1);
16571
16572 if (node->IsRootNode() || node->IsCentralNode())
16573 {
16574 // 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.
16575 ImGuiDockNode* new_node = DockContextAddNode(ctx, 0);
16576 new_node->Pos = node->Pos;
16577 new_node->Size = node->Size;
16578 new_node->SizeRef = node->SizeRef;
16579 DockNodeMoveWindows(new_node, node);
16580 DockSettingsRenameNodeReferences(node->ID, new_node->ID);
16581 node = new_node;
16582 }
16583 else
16584 {
16585 // Otherwise extract our node and merge our sibling back into the parent node.
16586 IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
16587 int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1;
16588 node->ParentNode->ChildNodes[index_in_parent] = NULL;
16589 DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]);
16590 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
16591 node->ParentNode = NULL;
16592 }
16593 for (ImGuiWindow* window : node->Windows)
16594 {
16595 window->Flags &= ~ImGuiWindowFlags_ChildWindow;
16596 if (window->ParentWindow)
16597 window->ParentWindow->DC.ChildWindows.find_erase(window);
16598 UpdateWindowParentAndRootLinks(window, window->Flags, NULL);
16599 }
16600 node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode;
16601 node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport);
16602 node->WantMouseMove = true;
16603 MarkIniSettingsDirty();
16604}
16605
16606// This is mostly used for automation.
16607bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos)
16608{
16609 if (target != NULL && target_node == NULL)
16610 target_node = target->DockNode;
16611
16612 // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects
16613 // (which would be functionally identical) we only show the outer one. Reflect this here.
16614 if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None)
16615 split_outer = true;
16616 ImGuiDockPreviewData split_data;
16617 DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer);
16618 if (split_data.DropRectsDraw[split_dir+1].IsInverted())
16619 return false;
16620 *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter();
16621 return true;
16622}
16623
16624//-----------------------------------------------------------------------------
16625// Docking: ImGuiDockNode
16626//-----------------------------------------------------------------------------
16627// - DockNodeGetTabOrder()
16628// - DockNodeAddWindow()
16629// - DockNodeRemoveWindow()
16630// - DockNodeMoveChildNodes()
16631// - DockNodeMoveWindows()
16632// - DockNodeApplyPosSizeToWindows()
16633// - DockNodeHideHostWindow()
16634// - ImGuiDockNodeFindInfoResults
16635// - DockNodeFindInfo()
16636// - DockNodeFindWindowByID()
16637// - DockNodeUpdateFlagsAndCollapse()
16638// - DockNodeUpdateHasCentralNodeFlag()
16639// - DockNodeUpdateVisibleFlag()
16640// - DockNodeStartMouseMovingWindow()
16641// - DockNodeUpdate()
16642// - DockNodeUpdateWindowMenu()
16643// - DockNodeBeginAmendTabBar()
16644// - DockNodeEndAmendTabBar()
16645// - DockNodeUpdateTabBar()
16646// - DockNodeAddTabBar()
16647// - DockNodeRemoveTabBar()
16648// - DockNodeIsDropAllowedOne()
16649// - DockNodeIsDropAllowed()
16650// - DockNodeCalcTabBarLayout()
16651// - DockNodeCalcSplitRects()
16652// - DockNodeCalcDropRectsAndTestMousePos()
16653// - DockNodePreviewDockSetup()
16654// - DockNodePreviewDockRender()
16655//-----------------------------------------------------------------------------
16656
16657ImGuiDockNode::ImGuiDockNode(ImGuiID id)
16658{
16659 ID = id;
16660 SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None;
16661 ParentNode = ChildNodes[0] = ChildNodes[1] = NULL;
16662 TabBar = NULL;
16663 SplitAxis = ImGuiAxis_None;
16664
16665 State = ImGuiDockNodeState_Unknown;
16666 LastBgColor = IM_COL32_WHITE;
16667 HostWindow = VisibleWindow = NULL;
16668 CentralNode = OnlyNodeWithWindows = NULL;
16669 CountNodeWithWindows = 0;
16670 LastFrameAlive = LastFrameActive = LastFrameFocused = -1;
16671 LastFocusedNodeId = 0;
16672 SelectedTabId = 0;
16673 WantCloseTabId = 0;
16674 RefViewportId = 0;
16675 AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode;
16676 AuthorityForViewport = ImGuiDataAuthority_Auto;
16677 IsVisible = true;
16678 IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false;
16679 IsBgDrawnThisFrame = false;
16680 WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false;
16681}
16682
16683ImGuiDockNode::~ImGuiDockNode()
16684{
16685 IM_DELETE(TabBar);
16686 TabBar = NULL;
16687 ChildNodes[0] = ChildNodes[1] = NULL;
16688}
16689
16690int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
16691{
16692 ImGuiTabBar* tab_bar = window->DockNode->TabBar;
16693 if (tab_bar == NULL)
16694 return -1;
16695 ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId);
16696 return tab ? TabBarGetTabOrder(tab_bar, tab) : -1;
16697}
16698
16699static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window)
16700{
16701 window->Hidden = true;
16702 window->HiddenFramesCanSkipItems = window->Active ? 1 : 2;
16703}
16704
16705static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar)
16706{
16707 ImGuiContext& g = *GImGui; (void)g;
16708 if (window->DockNode)
16709 {
16710 // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node)
16711 IM_ASSERT(window->DockNode->ID != node->ID);
16712 DockNodeRemoveWindow(window->DockNode, window, 0);
16713 }
16714 IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL);
16715 IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name);
16716
16717 // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window,
16718 // 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).
16719 // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin()
16720 if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false)
16721 DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]);
16722
16723 node->Windows.push_back(window);
16724 node->WantHiddenTabBarUpdate = true;
16725 window->DockNode = node;
16726 window->DockId = node->ID;
16727 window->DockIsActive = (node->Windows.Size > 1);
16728 window->DockTabWantClose = false;
16729
16730 // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage.
16731 // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one.
16732 if (node->HostWindow == NULL && node->IsFloatingNode())
16733 {
16734 if (node->AuthorityForPos == ImGuiDataAuthority_Auto)
16735 node->AuthorityForPos = ImGuiDataAuthority_Window;
16736 if (node->AuthorityForSize == ImGuiDataAuthority_Auto)
16737 node->AuthorityForSize = ImGuiDataAuthority_Window;
16738 if (node->AuthorityForViewport == ImGuiDataAuthority_Auto)
16739 node->AuthorityForViewport = ImGuiDataAuthority_Window;
16740 }
16741
16742 // Add to tab bar if requested
16743 if (add_to_tab_bar)
16744 {
16745 if (node->TabBar == NULL)
16746 {
16747 DockNodeAddTabBar(node);
16748 node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId;
16749
16750 // Add existing windows
16751 for (int n = 0; n < node->Windows.Size - 1; n++)
16752 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]);
16753 }
16754 TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window);
16755 }
16756
16757 DockNodeUpdateVisibleFlag(node);
16758
16759 // 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.
16760 if (node->HostWindow)
16761 UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow);
16762}
16763
16764static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id)
16765{
16766 ImGuiContext& g = *GImGui;
16767 IM_ASSERT(window->DockNode == node);
16768 //IM_ASSERT(window->RootWindowDockTree == node->HostWindow);
16769 //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin()
16770 IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID);
16771 IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name);
16772
16773 window->DockNode = NULL;
16774 window->DockIsActive = window->DockTabWantClose = false;
16775 window->DockId = save_dock_id;
16776 window->Flags &= ~ImGuiWindowFlags_ChildWindow;
16777 if (window->ParentWindow)
16778 window->ParentWindow->DC.ChildWindows.find_erase(window);
16779 UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately
16780
16781 if (node->HostWindow && node->HostWindow->ViewportOwned)
16782 {
16783 // When undocking from a user interaction this will always run in NewFrame() and have not much effect.
16784 // But mid-frame, if we clear viewport we need to mark window as hidden as well.
16785 window->Viewport = NULL;
16786 window->ViewportId = 0;
16787 window->ViewportOwned = false;
16788 window->Hidden = true;
16789 }
16790
16791 // Remove window
16792 bool erased = false;
16793 for (int n = 0; n < node->Windows.Size; n++)
16794 if (node->Windows[n] == window)
16795 {
16796 node->Windows.erase(node->Windows.Data + n);
16797 erased = true;
16798 break;
16799 }
16800 if (!erased)
16801 IM_ASSERT(erased);
16802 if (node->VisibleWindow == window)
16803 node->VisibleWindow = NULL;
16804
16805 // Remove tab and possibly tab bar
16806 node->WantHiddenTabBarUpdate = true;
16807 if (node->TabBar)
16808 {
16809 TabBarRemoveTab(node->TabBar, window->TabId);
16810 const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2;
16811 if (node->Windows.Size < tab_count_threshold_for_tab_bar)
16812 DockNodeRemoveTabBar(node);
16813 }
16814
16815 if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID)
16816 {
16817 // Automatic dock node delete themselves if they are not holding at least one tab
16818 DockContextRemoveNode(&g, node, true);
16819 return;
16820 }
16821
16822 if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow)
16823 {
16824 ImGuiWindow* remaining_window = node->Windows[0];
16825 // Note: we used to transport viewport ownership here.
16826 remaining_window->Collapsed = node->HostWindow->Collapsed;
16827 }
16828
16829 // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree
16830 DockNodeUpdateVisibleFlag(node);
16831}
16832
16833static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
16834{
16835 IM_ASSERT(dst_node->Windows.Size == 0);
16836 dst_node->ChildNodes[0] = src_node->ChildNodes[0];
16837 dst_node->ChildNodes[1] = src_node->ChildNodes[1];
16838 if (dst_node->ChildNodes[0])
16839 dst_node->ChildNodes[0]->ParentNode = dst_node;
16840 if (dst_node->ChildNodes[1])
16841 dst_node->ChildNodes[1]->ParentNode = dst_node;
16842 dst_node->SplitAxis = src_node->SplitAxis;
16843 dst_node->SizeRef = src_node->SizeRef;
16844 src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL;
16845}
16846
16847static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
16848{
16849 // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
16850 IM_ASSERT(src_node && dst_node && dst_node != src_node);
16851 ImGuiTabBar* src_tab_bar = src_node->TabBar;
16852 if (src_tab_bar != NULL)
16853 IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size);
16854
16855 // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.)
16856 bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL);
16857 if (move_tab_bar)
16858 {
16859 dst_node->TabBar = src_node->TabBar;
16860 src_node->TabBar = NULL;
16861 }
16862
16863 // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar().
16864 for (ImGuiWindow* window : src_node->Windows)
16865 {
16866 window->DockNode = NULL;
16867 window->DockIsActive = false;
16868 DockNodeAddWindow(dst_node, window, !move_tab_bar);
16869 }
16870 src_node->Windows.clear();
16871
16872 if (!move_tab_bar && src_node->TabBar)
16873 {
16874 if (dst_node->TabBar)
16875 dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId;
16876 DockNodeRemoveTabBar(src_node);
16877 }
16878}
16879
16880static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node)
16881{
16882 for (ImGuiWindow* window : node->Windows)
16883 {
16884 SetWindowPos(window, node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame
16885 SetWindowSize(window, node->Size, ImGuiCond_Always);
16886 }
16887}
16888
16889static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
16890{
16891 if (node->HostWindow)
16892 {
16893 if (node->HostWindow->DockNodeAsHost == node)
16894 node->HostWindow->DockNodeAsHost = NULL;
16895 node->HostWindow = NULL;
16896 }
16897
16898 if (node->Windows.Size == 1)
16899 {
16900 node->VisibleWindow = node->Windows[0];
16901 node->Windows[0]->DockIsActive = false;
16902 }
16903
16904 if (node->TabBar)
16905 DockNodeRemoveTabBar(node);
16906}
16907
16908// Search function called once by root node in DockNodeUpdate()
16910{
16911 ImGuiDockNode* CentralNode;
16912 ImGuiDockNode* FirstNodeWithWindows;
16914 //ImGuiWindowClass WindowClassForMerges;
16915
16916 ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); }
16917};
16918
16919static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info)
16920{
16921 if (node->Windows.Size > 0)
16922 {
16923 if (info->FirstNodeWithWindows == NULL)
16924 info->FirstNodeWithWindows = node;
16925 info->CountNodesWithWindows++;
16926 }
16927 if (node->IsCentralNode())
16928 {
16929 IM_ASSERT(info->CentralNode == NULL); // Should be only one
16930 IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this.");
16931 info->CentralNode = node;
16932 }
16933 if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL)
16934 return;
16935 if (node->ChildNodes[0])
16936 DockNodeFindInfo(node->ChildNodes[0], info);
16937 if (node->ChildNodes[1])
16938 DockNodeFindInfo(node->ChildNodes[1], info);
16939}
16940
16941static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id)
16942{
16943 IM_ASSERT(id != 0);
16944 for (ImGuiWindow* window : node->Windows)
16945 if (window->ID == id)
16946 return window;
16947 return NULL;
16948}
16949
16950// - Remove inactive windows/nodes.
16951// - Update visibility flag.
16952static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node)
16953{
16954 ImGuiContext& g = *GImGui;
16955 IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node);
16956
16957 // Inherit most flags
16958 if (node->ParentNode)
16959 node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
16960
16961 // Recurse into children
16962 // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'.
16963 // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node'
16964 // 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)
16965 node->HasCentralNodeChild = false;
16966 if (node->ChildNodes[0])
16967 DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]);
16968 if (node->ChildNodes[1])
16969 DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]);
16970
16971 // Remove inactive windows, collapse nodes
16972 // Merge node flags overrides stored in windows
16973 node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
16974 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
16975 {
16976 ImGuiWindow* window = node->Windows[window_n];
16977 IM_ASSERT(window->DockNode == node);
16978
16979 bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
16980 bool remove = false;
16981 remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount);
16982 remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame
16983 remove |= (window->DockTabWantClose);
16984 if (remove)
16985 {
16986 window->DockTabWantClose = false;
16987 if (node->Windows.Size == 1 && !node->IsCentralNode())
16988 {
16989 DockNodeHideHostWindow(node);
16990 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
16991 DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return
16992 return;
16993 }
16994 DockNodeRemoveWindow(node, window, node->ID);
16995 window_n--;
16996 continue;
16997 }
16998
16999 // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this.
17000 //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear;
17001 node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet;
17002 }
17003 node->UpdateMergedFlags();
17004
17005 // Auto-hide tab bar option
17006 ImGuiDockNodeFlags node_flags = node->MergedFlags;
17007 if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar())
17008 node->WantHiddenTabBarToggle = true;
17009 node->WantHiddenTabBarUpdate = false;
17010
17011 // Cancel toggling if we know our tab bar is enforced to be hidden at all times
17012 if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar))
17013 node->WantHiddenTabBarToggle = false;
17014
17015 // Apply toggles at a single point of the frame (here!)
17016 if (node->Windows.Size > 1)
17017 node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar);
17018 else if (node->WantHiddenTabBarToggle)
17019 node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar);
17020 node->WantHiddenTabBarToggle = false;
17021
17022 DockNodeUpdateVisibleFlag(node);
17023}
17024
17025// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames.
17026static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node)
17027{
17028 node->HasCentralNodeChild = false;
17029 if (node->ChildNodes[0])
17030 DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]);
17031 if (node->ChildNodes[1])
17032 DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]);
17033 if (node->IsRootNode())
17034 {
17035 ImGuiDockNode* mark_node = node->CentralNode;
17036 while (mark_node)
17037 {
17038 mark_node->HasCentralNodeChild = true;
17039 mark_node = mark_node->ParentNode;
17040 }
17041 }
17042}
17043
17044static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node)
17045{
17046 // Update visibility flag
17047 bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode();
17048 is_visible |= (node->Windows.Size > 0);
17049 is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible);
17050 is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible);
17051 node->IsVisible = is_visible;
17052}
17053
17054static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window)
17055{
17056 ImGuiContext& g = *GImGui;
17057 IM_ASSERT(node->WantMouseMove == true);
17058 StartMouseMovingWindow(window);
17059 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos;
17060 g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision.
17061 node->WantMouseMove = false;
17062}
17063
17064// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class.
17065static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node)
17066{
17067 DockNodeUpdateFlagsAndCollapse(node);
17068
17069 // - Setup central node pointers
17070 // - 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!)
17071 // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing
17073 DockNodeFindInfo(node, &info);
17074 node->CentralNode = info.CentralNode;
17075 node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL;
17076 node->CountNodeWithWindows = info.CountNodesWithWindows;
17077 if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL)
17078 node->LastFocusedNodeId = info.FirstNodeWithWindows->ID;
17079
17080 // Copy the window class from of our first window so it can be used for proper dock filtering.
17081 // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy.
17082 // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec.
17083 if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows)
17084 {
17085 node->WindowClass = first_node_with_windows->Windows[0]->WindowClass;
17086 for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
17087 if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false)
17088 {
17089 node->WindowClass = first_node_with_windows->Windows[n]->WindowClass;
17090 break;
17091 }
17092 }
17093
17094 ImGuiDockNode* mark_node = node->CentralNode;
17095 while (mark_node)
17096 {
17097 mark_node->HasCentralNodeChild = true;
17098 mark_node = mark_node->ParentNode;
17099 }
17100}
17101
17102static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window)
17103{
17104 // Remove ourselves from any previous different host window
17105 // This can happen if a user mistakenly does (see #4295 for details):
17106 // - N+0: DockBuilderAddNode(id, 0) // missing ImGuiDockNodeFlags_DockSpace
17107 // - N+1: NewFrame() // will create floating host window for that node
17108 // - N+1: DockSpace(id) // requalify node as dockspace, moving host window
17109 if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node)
17110 node->HostWindow->DockNodeAsHost = NULL;
17111
17112 host_window->DockNodeAsHost = node;
17113 node->HostWindow = host_window;
17114}
17115
17116static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
17117{
17118 ImGuiContext& g = *GImGui;
17119 IM_ASSERT(node->LastFrameActive != g.FrameCount);
17120 node->LastFrameAlive = g.FrameCount;
17121 node->IsBgDrawnThisFrame = false;
17122
17123 node->CentralNode = node->OnlyNodeWithWindows = NULL;
17124 if (node->IsRootNode())
17125 DockNodeUpdateForRootNode(node);
17126
17127 // Remove tab bar if not needed
17128 if (node->TabBar && node->IsNoTabBar())
17129 DockNodeRemoveTabBar(node);
17130
17131 // 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)
17132 bool want_to_hide_host_window = false;
17133 if (node->IsFloatingNode())
17134 {
17135 if (node->Windows.Size <= 1 && node->IsLeafNode())
17136 if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar))
17137 want_to_hide_host_window = true;
17138 if (node->CountNodeWithWindows == 0)
17139 want_to_hide_host_window = true;
17140 }
17141 if (want_to_hide_host_window)
17142 {
17143 if (node->Windows.Size == 1)
17144 {
17145 // Floating window pos/size is authoritative
17146 ImGuiWindow* single_window = node->Windows[0];
17147 node->Pos = single_window->Pos;
17148 node->Size = single_window->SizeFull;
17149 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
17150
17151 // Transfer focus immediately so when we revert to a regular window it is immediately selected
17152 if (node->HostWindow && g.NavWindow == node->HostWindow)
17153 FocusWindow(single_window);
17154 if (node->HostWindow)
17155 {
17156 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);
17157 single_window->Viewport = node->HostWindow->Viewport;
17158 single_window->ViewportId = node->HostWindow->ViewportId;
17159 if (node->HostWindow->ViewportOwned)
17160 {
17161 single_window->Viewport->ID = single_window->ID;
17162 single_window->Viewport->Window = single_window;
17163 single_window->ViewportOwned = true;
17164 }
17165 }
17166 node->RefViewportId = single_window->ViewportId;
17167 }
17168
17169 DockNodeHideHostWindow(node);
17170 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow;
17171 node->WantCloseAll = false;
17172 node->WantCloseTabId = 0;
17173 node->HasCloseButton = node->HasWindowMenuButton = false;
17174 node->LastFrameActive = g.FrameCount;
17175
17176 if (node->WantMouseMove && node->Windows.Size == 1)
17177 DockNodeStartMouseMovingWindow(node, node->Windows[0]);
17178 return;
17179 }
17180
17181 // In some circumstance we will defer creating the host window (so everything will be kept hidden),
17182 // while the expected visible window is resizing itself.
17183 // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled,
17184 // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up:
17185 // N+0: Begin(): window created (with no known size), node is created
17186 // N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible
17187 // N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible
17188 // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code.
17189 // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin().
17190 // In reality it isn't very important as user quickly ends up with size data in .ini file.
17191 if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode())
17192 {
17193 IM_ASSERT(node->Windows.Size > 0);
17194 ImGuiWindow* ref_window = NULL;
17195 if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them!
17196 ref_window = DockNodeFindWindowByID(node, node->SelectedTabId);
17197 if (ref_window == NULL)
17198 ref_window = node->Windows[0];
17199 if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0)
17200 {
17201 node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing;
17202 return;
17203 }
17204 }
17205
17206 const ImGuiDockNodeFlags node_flags = node->MergedFlags;
17207
17208 // Decide if the node will have a close button and a window menu button
17209 node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0;
17210 node->HasCloseButton = false;
17211 for (ImGuiWindow* window : node->Windows)
17212 {
17213 // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call.
17214 node->HasCloseButton |= window->HasCloseButton;
17215 window->DockIsActive = (node->Windows.Size > 1);
17216 }
17217 if (node_flags & ImGuiDockNodeFlags_NoCloseButton)
17218 node->HasCloseButton = false;
17219
17220 // Bind or create host window
17221 ImGuiWindow* host_window = NULL;
17222 bool beginned_into_host_window = false;
17223 if (node->IsDockSpace())
17224 {
17225 // [Explicit root dockspace node]
17226 IM_ASSERT(node->HostWindow);
17227 host_window = node->HostWindow;
17228 }
17229 else
17230 {
17231 // [Automatic root or child nodes]
17232 if (node->IsRootNode() && node->IsVisible)
17233 {
17234 ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
17235
17236 // Sync Pos
17237 if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window)
17238 SetNextWindowPos(ref_window->Pos);
17239 else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode)
17240 SetNextWindowPos(node->Pos);
17241
17242 // Sync Size
17243 if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
17244 SetNextWindowSize(ref_window->SizeFull);
17245 else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode)
17246 SetNextWindowSize(node->Size);
17247
17248 // Sync Collapsed
17249 if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window)
17250 SetNextWindowCollapsed(ref_window->Collapsed);
17251
17252 // Sync Viewport
17253 if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window)
17254 SetNextWindowViewport(ref_window->ViewportId);
17255 else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0)
17256 SetNextWindowViewport(node->RefViewportId);
17257
17258 SetNextWindowClass(&node->WindowClass);
17259
17260 // Begin into the host window
17261 char window_label[20];
17262 DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label));
17263 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost;
17264 window_flags |= ImGuiWindowFlags_NoFocusOnAppearing;
17265 window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse;
17266 window_flags |= ImGuiWindowFlags_NoTitleBar;
17267
17268 SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders
17269 PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
17270 Begin(window_label, NULL, window_flags);
17271 PopStyleVar();
17272 beginned_into_host_window = true;
17273
17274 host_window = g.CurrentWindow;
17275 DockNodeSetupHostWindow(node, host_window);
17276 host_window->DC.CursorPos = host_window->Pos;
17277 node->Pos = host_window->Pos;
17278 node->Size = host_window->Size;
17279
17280 // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow)
17281 // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags.
17282 // 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.
17283 // 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
17284 // 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
17285 // after the dock host window, losing their top-most status.
17286 if (node->HostWindow->Appearing)
17287 BringWindowToDisplayFront(node->HostWindow);
17288
17289 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
17290 }
17291 else if (node->ParentNode)
17292 {
17293 node->HostWindow = host_window = node->ParentNode->HostWindow;
17294 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto;
17295 }
17296 if (node->WantMouseMove && node->HostWindow)
17297 DockNodeStartMouseMovingWindow(node, node->HostWindow);
17298 }
17299 node->RefViewportId = 0; // Clear when we have a host window
17300
17301 // Update focused node (the one whose title bar is highlight) within a node tree
17302 if (node->IsSplitNode())
17303 IM_ASSERT(node->TabBar == NULL);
17304 if (node->IsRootNode())
17305 if (ImGuiWindow* p_window = g.NavWindow ? g.NavWindow->RootWindow : NULL)
17306 while (p_window != NULL && p_window->DockNode != NULL)
17307 {
17308 ImGuiDockNode* p_node = DockNodeGetRootNode(p_window->DockNode);
17309 if (p_node == node)
17310 {
17311 node->LastFocusedNodeId = p_window->DockNode->ID; // Note: not using root node ID!
17312 break;
17313 }
17314 p_window = p_node->HostWindow ? p_node->HostWindow->RootWindow : NULL;
17315 }
17316
17317 // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace
17318 ImGuiDockNode* central_node = node->CentralNode;
17319 const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty();
17320 bool central_node_hole_register_hit_test_hole = central_node_hole;
17321 if (central_node_hole)
17322 if (const ImGuiPayload* payload = ImGui::GetDragDropPayload())
17323 if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data))
17324 central_node_hole_register_hit_test_hole = false;
17325 if (central_node_hole_register_hit_test_hole)
17326 {
17327 // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily.
17328 // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen
17329 // covering passthru node we'd have a gap on the edge not covered by the hole)
17330 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
17331 ImGuiDockNode* root_node = DockNodeGetRootNode(central_node);
17332 ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size);
17333 ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size);
17334 if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += WINDOWS_HOVER_PADDING; }
17335 if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_HOVER_PADDING; }
17336 if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_HOVER_PADDING; }
17337 if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_HOVER_PADDING; }
17338 //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255));
17339 if (central_node_hole && !hole_rect.IsInverted())
17340 {
17341 SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min);
17342 if (host_window->ParentWindow)
17343 SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min);
17344 }
17345 }
17346
17347 // Update position/size, process and draw resizing splitters
17348 if (node->IsRootNode() && host_window)
17349 {
17350 DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size);
17351 PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]);
17352 PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]);
17353 PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]);
17354 DockNodeTreeUpdateSplitter(node);
17355 PopStyleColor(3);
17356 }
17357
17358 // Draw empty node background (currently can only be the Central Node)
17359 if (host_window && node->IsEmpty() && node->IsVisible)
17360 {
17361 host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
17362 node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg);
17363 if (node->LastBgColor != 0)
17364 host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor);
17365 node->IsBgDrawnThisFrame = true;
17366 }
17367
17368 // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set.
17369 // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size
17370 // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order!
17371 const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0;
17372 if (render_dockspace_bg && node->IsVisible)
17373 {
17374 host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG);
17375 if (central_node_hole)
17376 RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f);
17377 else
17378 host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f);
17379 }
17380
17381 // Draw and populate Tab Bar
17382 if (host_window)
17383 host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG);
17384 if (host_window && node->Windows.Size > 0)
17385 {
17386 DockNodeUpdateTabBar(node, host_window);
17387 }
17388 else
17389 {
17390 node->WantCloseAll = false;
17391 node->WantCloseTabId = 0;
17392 node->IsFocused = false;
17393 }
17394 if (node->TabBar && node->TabBar->SelectedTabId)
17395 node->SelectedTabId = node->TabBar->SelectedTabId;
17396 else if (node->Windows.Size > 0)
17397 node->SelectedTabId = node->Windows[0]->TabId;
17398
17399 // Draw payload drop target
17400 if (host_window && node->IsVisible)
17401 if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window))
17402 BeginDockableDragDropTarget(host_window);
17403
17404 // We update this after DockNodeUpdateTabBar()
17405 node->LastFrameActive = g.FrameCount;
17406
17407 // Recurse into children
17408 // FIXME-DOCK FIXME-OPT: Should not need to recurse into children
17409 if (host_window)
17410 {
17411 if (node->ChildNodes[0])
17412 DockNodeUpdate(node->ChildNodes[0]);
17413 if (node->ChildNodes[1])
17414 DockNodeUpdate(node->ChildNodes[1]);
17415
17416 // Render outer borders last (after the tab bar)
17417 if (node->IsRootNode())
17418 RenderWindowOuterBorders(host_window);
17419 }
17420
17421 // End host window
17422 if (beginned_into_host_window) //-V1020
17423 End();
17424}
17425
17426// 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.
17427static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs)
17428{
17429 ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window;
17430 ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window;
17431 if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder))
17432 return d;
17433 return (a->BeginOrderWithinContext - b->BeginOrderWithinContext);
17434}
17435
17436// Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node.
17437// This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented)
17438// Custom overrides may want to decorate, group, sort entries.
17439// Please note those are internal structures: if you copy this expect occasional breakage.
17440// (if you don't need to modify the "Tabs.Size == 1" behavior/path it is recommend you call this function in your handler)
17441void ImGui::DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar)
17442{
17443 IM_UNUSED(ctx);
17444 if (tab_bar->Tabs.Size == 1)
17445 {
17446 // "Hide tab bar" option. Being one of our rare user-facing string we pull it from a table.
17447 if (MenuItem(LocalizeGetMsg(ImGuiLocKey_DockingHideTabBar), NULL, node->IsHiddenTabBar()))
17448 node->WantHiddenTabBarToggle = true;
17449 }
17450 else
17451 {
17452 // Display a selectable list of windows in this docking node
17453 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
17454 {
17455 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17456 if (tab->Flags & ImGuiTabItemFlags_Button)
17457 continue;
17458 if (Selectable(TabBarGetTabName(tab_bar, tab), tab->ID == tab_bar->SelectedTabId))
17459 TabBarQueueFocus(tab_bar, tab);
17460 SameLine();
17461 Text(" ");
17462 }
17463 }
17464}
17465
17466static void ImGui::DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar)
17467{
17468 // Try to position the menu so it is more likely to stays within the same viewport
17469 ImGuiContext& g = *GImGui;
17470 if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left)
17471 SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f));
17472 else
17473 SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f));
17474 if (BeginPopup("#WindowMenu"))
17475 {
17476 node->IsFocused = true;
17477 g.DockNodeWindowMenuHandler(&g, node, tab_bar);
17478 EndPopup();
17479 }
17480}
17481
17482// User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button.
17483bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node)
17484{
17485 if (node->TabBar == NULL || node->HostWindow == NULL)
17486 return false;
17487 if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
17488 return false;
17489 if (node->TabBar->ID == 0)
17490 return false;
17491 Begin(node->HostWindow->Name);
17492 PushOverrideID(node->ID);
17493 bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags);
17494 IM_UNUSED(ret);
17495 IM_ASSERT(ret);
17496 return true;
17497}
17498
17499void ImGui::DockNodeEndAmendTabBar()
17500{
17501 EndTabBar();
17502 PopID();
17503 End();
17504}
17505
17506static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node)
17507{
17508 // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy)
17509 ImGuiContext& g = *GImGui;
17510 if (g.NavWindowingTarget)
17511 return (g.NavWindowingTarget->DockNode == node);
17512
17513 // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window)
17514 if (g.NavWindow && root_node->LastFocusedNodeId == node->ID)
17515 {
17516 // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node)
17517 ImGuiWindow* parent_window = g.NavWindow->RootWindow;
17518 while (parent_window->Flags & ImGuiWindowFlags_ChildMenu)
17519 parent_window = parent_window->ParentWindow->RootWindow;
17520 ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode;
17521 for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL)
17522 if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node)
17523 return true;
17524 }
17525 return false;
17526}
17527
17528// Submit the tab bar corresponding to a dock node and various housekeeping details.
17529static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window)
17530{
17531 ImGuiContext& g = *GImGui;
17532 ImGuiStyle& style = g.Style;
17533
17534 const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount);
17535 const bool closed_all = node->WantCloseAll && node_was_active;
17536 const ImGuiID closed_one = node->WantCloseTabId && node_was_active;
17537 node->WantCloseAll = false;
17538 node->WantCloseTabId = 0;
17539
17540 // Decide if we should use a focused title bar color
17541 bool is_focused = false;
17542 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
17543 if (IsDockNodeTitleBarHighlighted(node, root_node))
17544 is_focused = true;
17545
17546 // Hidden tab bar will show a triangle on the upper-left (in Begin)
17547 if (node->IsHiddenTabBar() || node->IsNoTabBar())
17548 {
17549 node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL;
17550 node->IsFocused = is_focused;
17551 if (is_focused)
17552 node->LastFrameFocused = g.FrameCount;
17553 if (node->VisibleWindow)
17554 {
17555 // Notify root of visible window (used to display title in OS task bar)
17556 if (is_focused || root_node->VisibleWindow == NULL)
17557 root_node->VisibleWindow = node->VisibleWindow;
17558 if (node->TabBar)
17559 node->TabBar->VisibleTabId = node->VisibleWindow->TabId;
17560 }
17561 return;
17562 }
17563
17564 // 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
17565 bool backup_skip_item = host_window->SkipItems;
17566 if (!node->IsDockSpace())
17567 {
17568 host_window->SkipItems = false;
17569 host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
17570 }
17571
17572 // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID.
17573 // 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,
17574 // as docked windows themselves will override the stack with their own root ID.
17575 PushOverrideID(node->ID);
17576 ImGuiTabBar* tab_bar = node->TabBar;
17577 bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden
17578 if (tab_bar == NULL)
17579 {
17580 DockNodeAddTabBar(node);
17581 tab_bar = node->TabBar;
17582 }
17583
17584 ImGuiID focus_tab_id = 0;
17585 node->IsFocused = is_focused;
17586
17587 const ImGuiDockNodeFlags node_flags = node->MergedFlags;
17588 const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None);
17589
17590 // In a dock node, the Collapse Button turns into the Window Menu button.
17591 // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes?
17592 if (has_window_menu_button && IsPopupOpen("#WindowMenu"))
17593 {
17594 ImGuiID next_selected_tab_id = tab_bar->NextSelectedTabId;
17595 DockNodeWindowMenuUpdate(node, tab_bar);
17596 if (tab_bar->NextSelectedTabId != 0 && tab_bar->NextSelectedTabId != next_selected_tab_id)
17597 focus_tab_id = tab_bar->NextSelectedTabId;
17598 is_focused |= node->IsFocused;
17599 }
17600
17601 // Layout
17602 ImRect title_bar_rect, tab_bar_rect;
17603 ImVec2 window_menu_button_pos;
17604 ImVec2 close_button_pos;
17605 DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos);
17606
17607 // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value.
17608 const int tabs_count_old = tab_bar->Tabs.Size;
17609 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
17610 {
17611 ImGuiWindow* window = node->Windows[window_n];
17612 if (TabBarFindTabByID(tab_bar, window->TabId) == NULL)
17613 TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window);
17614 }
17615
17616 // Title bar
17617 if (is_focused)
17618 node->LastFrameFocused = g.FrameCount;
17619 ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
17620 ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize);
17621 host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags);
17622
17623 // Docking/Collapse button
17624 if (has_window_menu_button)
17625 {
17626 if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node)
17627 OpenPopup("#WindowMenu");
17628 if (IsItemActive())
17629 focus_tab_id = tab_bar->SelectedTabId;
17630 if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f)
17631 SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode));
17632 }
17633
17634 // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value
17635 int tabs_unsorted_start = tab_bar->Tabs.Size;
17636 for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--)
17637 {
17638 // 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?
17639 tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted;
17640 tabs_unsorted_start = tab_n;
17641 }
17642 if (tab_bar->Tabs.Size > tabs_unsorted_start)
17643 {
17644 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)" : "");
17645 for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++)
17646 {
17647 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17648 IMGUI_DEBUG_LOG_DOCKING("[docking] - Tab 0x%08X '%s' Order %d\n", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1);
17649 }
17650 IMGUI_DEBUG_LOG_DOCKING("[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\n", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1);
17651 if (tab_bar->Tabs.Size > tabs_unsorted_start + 1)
17652 ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder);
17653 }
17654
17655 // Apply NavWindow focus back to the tab bar
17656 if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node)
17657 tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId;
17658
17659 // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated
17660 if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL)
17661 tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId;
17662 else if (tab_bar->Tabs.Size > tabs_count_old)
17663 tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId;
17664
17665 // Begin tab bar
17666 ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons);
17667 tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;// | ImGuiTabBarFlags_FittingPolicyScroll;
17668 if (!host_window->Collapsed && is_focused)
17669 tab_bar_flags |= ImGuiTabBarFlags_IsFocused;
17670 tab_bar->ID = GetID("#TabBar");
17671 tab_bar->SeparatorMinX = node->Pos.x + host_window->WindowBorderSize; // Separator cover the whole node width
17672 tab_bar->SeparatorMaxX = node->Pos.x + node->Size.x - host_window->WindowBorderSize;
17673 BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags);
17674 //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255));
17675
17676 // Backup style colors
17677 ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT];
17678 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
17679 backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]];
17680
17681 // Submit actual tabs
17682 node->VisibleWindow = NULL;
17683 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
17684 {
17685 ImGuiWindow* window = node->Windows[window_n];
17686 if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument))
17687 continue;
17688 if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active)
17689 {
17690 ImGuiTabItemFlags tab_item_flags = 0;
17691 tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet;
17692 if (window->Flags & ImGuiWindowFlags_UnsavedDocument)
17693 tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument;
17694 if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
17695 tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
17696
17697 // Apply stored style overrides for the window
17698 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
17699 g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]);
17700
17701 // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so)
17702 bool tab_open = true;
17703 TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window);
17704 if (!tab_open)
17705 node->WantCloseTabId = window->TabId;
17706 if (tab_bar->VisibleTabId == window->TabId)
17707 node->VisibleWindow = window;
17708
17709 // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call
17710 window->DockTabItemStatusFlags = g.LastItemData.StatusFlags;
17711 window->DockTabItemRect = g.LastItemData.Rect;
17712
17713 // Update navigation ID on menu layer
17714 if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0)
17715 host_window->NavLastIds[1] = window->TabId;
17716 }
17717 }
17718
17719 // Restore style colors
17720 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
17721 g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n];
17722
17723 // Notify root of visible window (used to display title in OS task bar)
17724 if (node->VisibleWindow)
17725 if (is_focused || root_node->VisibleWindow == NULL)
17726 root_node->VisibleWindow = node->VisibleWindow;
17727
17728 // Close button (after VisibleWindow was updated)
17729 // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId
17730 const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton;
17731 const bool close_button_is_visible = node->HasCloseButton;
17732 //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)
17733 if (close_button_is_visible)
17734 {
17735 if (!close_button_is_enabled)
17736 {
17737 PushItemFlag(ImGuiItemFlags_Disabled, true);
17738 PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f));
17739 }
17740 if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos))
17741 {
17742 node->WantCloseAll = true;
17743 for (int n = 0; n < tab_bar->Tabs.Size; n++)
17744 TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]);
17745 }
17746 //if (IsItemActive())
17747 // focus_tab_id = tab_bar->SelectedTabId;
17748 if (!close_button_is_enabled)
17749 {
17750 PopStyleColor();
17751 PopItemFlag();
17752 }
17753 }
17754
17755 // When clicking on the title bar outside of tabs, we still focus the selected tab for that node
17756 // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them.
17757 ImGuiID title_bar_id = host_window->GetID("#TITLEBAR");
17758 if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id)
17759 {
17760 // AllowOverlap mode required for appending into dock node tab bar,
17761 // otherwise dragging window will steal HoveredId and amended tabs cannot get them.
17762 bool held;
17763 KeepAliveID(title_bar_id);
17764 ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap);
17765 if (g.HoveredId == title_bar_id)
17766 {
17767 g.LastItemData.ID = title_bar_id;
17768 }
17769 if (held)
17770 {
17771 if (IsMouseClicked(0))
17772 focus_tab_id = tab_bar->SelectedTabId;
17773
17774 // Forward moving request to selected window
17775 if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
17776 StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space
17777 }
17778 }
17779
17780 // Forward focus from host node to selected window
17781 //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget)
17782 // focus_tab_id = tab_bar->SelectedTabId;
17783
17784 // When clicked on a tab we requested focus to the docked child
17785 // This overrides the value set by "forward focus from host node to selected window".
17786 if (tab_bar->NextSelectedTabId)
17787 focus_tab_id = tab_bar->NextSelectedTabId;
17788
17789 // Apply navigation focus
17790 if (focus_tab_id != 0)
17791 if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id))
17792 if (tab->Window)
17793 {
17794 FocusWindow(tab->Window);
17795 NavInitWindow(tab->Window, false);
17796 }
17797
17798 EndTabBar();
17799 PopID();
17800
17801 // Restore SkipItems flag
17802 if (!node->IsDockSpace())
17803 {
17804 host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
17805 host_window->SkipItems = backup_skip_item;
17806 }
17807}
17808
17809static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node)
17810{
17811 IM_ASSERT(node->TabBar == NULL);
17812 node->TabBar = IM_NEW(ImGuiTabBar);
17813}
17814
17815static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node)
17816{
17817 if (node->TabBar == NULL)
17818 return;
17819 IM_DELETE(node->TabBar);
17820 node->TabBar = NULL;
17821}
17822
17823static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window)
17824{
17825 if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext)
17826 return false;
17827
17828 ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass;
17829 ImGuiWindowClass* payload_class = &payload->WindowClass;
17830 if (host_class->ClassId != payload_class->ClassId)
17831 {
17832 bool pass = false;
17833 if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0)
17834 pass = true;
17835 if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0)
17836 pass = true;
17837 if (!pass)
17838 return false;
17839 }
17840
17841 // Prevent docking any window created above a popup
17842 // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features),
17843 // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test.
17844 // But it would requires more work on our end because the dock host windows is technically created in NewFrame()
17845 // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking.
17846 ImGuiContext& g = *GImGui;
17847 for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)
17848 if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window)
17849 if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window)) // Payload is created from within a popup begin stack.
17850 return false;
17851
17852 return true;
17853}
17854
17855static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload)
17856{
17857 if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering
17858 return true;
17859
17860 const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1;
17861 for (int payload_n = 0; payload_n < payload_count; payload_n++)
17862 {
17863 ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload;
17864 if (DockNodeIsDropAllowedOne(payload, host_window))
17865 return true;
17866 }
17867 return false;
17868}
17869
17870// window menu button == collapse button when not in a dock node.
17871// FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code.
17872static 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)
17873{
17874 ImGuiContext& g = *GImGui;
17875 ImGuiStyle& style = g.Style;
17876
17877 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);
17878 if (out_title_rect) { *out_title_rect = r; }
17879
17880 r.Min.x += style.WindowBorderSize;
17881 r.Max.x -= style.WindowBorderSize;
17882
17883 float button_sz = g.FontSize;
17884 r.Min.x += style.FramePadding.x;
17885 r.Max.x -= style.FramePadding.x;
17886 ImVec2 window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y);
17887 if (node->HasCloseButton)
17888 {
17889 if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);
17890 r.Max.x -= button_sz + style.ItemInnerSpacing.x;
17891 }
17892 if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left)
17893 {
17894 r.Min.x += button_sz + style.ItemInnerSpacing.x;
17895 }
17896 else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right)
17897 {
17898 window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y);
17899 r.Max.x -= button_sz + style.ItemInnerSpacing.x;
17900 }
17901 if (out_tab_bar_rect) { *out_tab_bar_rect = r; }
17902 if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; }
17903}
17904
17905void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired)
17906{
17907 ImGuiContext& g = *GImGui;
17908 const float dock_spacing = g.Style.ItemInnerSpacing.x;
17909 const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
17910 pos_new[axis ^ 1] = pos_old[axis ^ 1];
17911 size_new[axis ^ 1] = size_old[axis ^ 1];
17912
17913 // Distribute size on given axis (with a desired size or equally)
17914 const float w_avail = size_old[axis] - dock_spacing;
17915 if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f)
17916 {
17917 size_new[axis] = size_new_desired[axis];
17918 size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);
17919 }
17920 else
17921 {
17922 size_new[axis] = IM_TRUNC(w_avail * 0.5f);
17923 size_old[axis] = IM_TRUNC(w_avail - size_new[axis]);
17924 }
17925
17926 // Position each node
17927 if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
17928 {
17929 pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing;
17930 }
17931 else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up)
17932 {
17933 pos_new[axis] = pos_old[axis];
17934 pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing;
17935 }
17936}
17937
17938// Retrieve the drop rectangles for a given direction or for the center + perform hit testing.
17939bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos)
17940{
17941 ImGuiContext& g = *GImGui;
17942
17943 const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight());
17944 const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f));
17945 float hs_w; // Half-size, longer axis
17946 float hs_h; // Half-size, smaller axis
17947 ImVec2 off; // Distance from edge or center
17948 if (outer_docking)
17949 {
17950 //hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f));
17951 //hs_h = ImTrunc(hs_w * 0.15f);
17952 //off = ImVec2(ImTrunc(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImTrunc(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h));
17953 hs_w = ImTrunc(hs_for_central_nodes * 1.50f);
17954 hs_h = ImTrunc(hs_for_central_nodes * 0.80f);
17955 off = ImTrunc(ImVec2(parent.GetWidth() * 0.5f - hs_h, parent.GetHeight() * 0.5f - hs_h));
17956 }
17957 else
17958 {
17959 hs_w = ImTrunc(hs_for_central_nodes);
17960 hs_h = ImTrunc(hs_for_central_nodes * 0.90f);
17961 off = ImTrunc(ImVec2(hs_w * 2.40f, hs_w * 2.40f));
17962 }
17963
17964 ImVec2 c = ImTrunc(parent.GetCenter());
17965 if (dir == ImGuiDir_None) { out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w); }
17966 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); }
17967 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); }
17968 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); }
17969 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); }
17970
17971 if (test_mouse_pos == NULL)
17972 return false;
17973
17974 ImRect hit_r = out_r;
17975 if (!outer_docking)
17976 {
17977 // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides
17978 hit_r.Expand(ImTrunc(hs_w * 0.30f));
17979 ImVec2 mouse_delta = (*test_mouse_pos - c);
17980 float mouse_delta_len2 = ImLengthSqr(mouse_delta);
17981 float r_threshold_center = hs_w * 1.4f;
17982 float r_threshold_sides = hs_w * (1.4f + 1.2f);
17983 if (mouse_delta_len2 < r_threshold_center * r_threshold_center)
17984 return (dir == ImGuiDir_None);
17985 if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides)
17986 return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y));
17987 }
17988 return hit_r.Contains(*test_mouse_pos);
17989}
17990
17991// host_node may be NULL if the window doesn't have a DockNode already.
17992// FIXME-DOCK: This is misnamed since it's also doing the filtering.
17993static 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)
17994{
17995 ImGuiContext& g = *GImGui;
17996
17997 // There is an edge case when docking into a dockspace which only has inactive nodes.
17998 // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive.
17999 // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference.
18000 if (payload_node == NULL)
18001 payload_node = payload_window->DockNodeAsHost;
18002 ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node;
18003 if (ref_node_for_rect)
18004 IM_ASSERT(ref_node_for_rect->IsVisible == true);
18005
18006 // Filter, figure out where we are allowed to dock
18007 ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet;
18008 ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet;
18009 data->IsCenterAvailable = true;
18010 if (is_outer_docking)
18011 data->IsCenterAvailable = false;
18012 else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe)
18013 data->IsCenterAvailable = false;
18014 else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) && host_node->IsCentralNode())
18015 data->IsCenterAvailable = false;
18016 else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split?
18017 data->IsCenterAvailable = false;
18018 else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty()))
18019 data->IsCenterAvailable = false;
18020 else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty())
18021 data->IsCenterAvailable = false;
18022
18023 data->IsSidesAvailable = true;
18024 if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplit) || g.IO.ConfigDockingNoSplit)
18025 data->IsSidesAvailable = false;
18026 else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode())
18027 data->IsSidesAvailable = false;
18028 else if (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther)
18029 data->IsSidesAvailable = false;
18030
18031 // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split)
18032 data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton);
18033 data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0);
18034 data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos;
18035 data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size;
18036
18037 // Calculate drop shapes geometry for allowed splitting directions
18038 IM_ASSERT(ImGuiDir_None == -1);
18039 data->SplitNode = host_node;
18040 data->SplitDir = ImGuiDir_None;
18041 data->IsSplitDirExplicit = false;
18042 if (!host_window->Collapsed)
18043 for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
18044 {
18045 if (dir == ImGuiDir_None && !data->IsCenterAvailable)
18046 continue;
18047 if (dir != ImGuiDir_None && !data->IsSidesAvailable)
18048 continue;
18049 if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos))
18050 {
18051 data->SplitDir = (ImGuiDir)dir;
18052 data->IsSplitDirExplicit = true;
18053 }
18054 }
18055
18056 // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar
18057 data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable);
18058 if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift)
18059 data->IsDropAllowed = false;
18060
18061 // Calculate split area
18062 data->SplitRatio = 0.0f;
18063 if (data->SplitDir != ImGuiDir_None)
18064 {
18065 ImGuiDir split_dir = data->SplitDir;
18066 ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
18067 ImVec2 pos_new, pos_old = data->FutureNode.Pos;
18068 ImVec2 size_new, size_old = data->FutureNode.Size;
18069 DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size);
18070
18071 // Calculate split ratio so we can pass it down the docking request
18072 float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]);
18073 data->FutureNode.Pos = pos_new;
18074 data->FutureNode.Size = size_new;
18075 data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio);
18076 }
18077}
18078
18079static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data)
18080{
18081 ImGuiContext& g = *GImGui;
18082 IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes
18083
18084 // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent.
18085 // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes.
18086 const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload;
18087
18088 // In case the two windows involved are on different viewports, we will draw the overlay on each of them.
18089 int overlay_draw_lists_count = 0;
18090 ImDrawList* overlay_draw_lists[2];
18091 overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport);
18092 if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload)
18093 overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport);
18094
18095 // Draw main preview rectangle
18096 const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f);
18097 const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f);
18098 const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f);
18099 const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f);
18100
18101 // Display area preview
18102 const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0);
18103 if (data->IsDropAllowed)
18104 {
18105 ImRect overlay_rect = data->FutureNode.Rect();
18106 if (data->SplitDir == ImGuiDir_None && can_preview_tabs)
18107 overlay_rect.Min.y += GetFrameHeight();
18108 if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable)
18109 for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
18110 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));
18111 }
18112
18113 // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read)
18114 if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable)
18115 {
18116 // Compute target tab bar geometry so we can locate our preview tabs
18117 ImRect tab_bar_rect;
18118 DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL);
18119 ImVec2 tab_pos = tab_bar_rect.Min;
18120 if (host_node && host_node->TabBar)
18121 {
18122 if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar())
18123 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.
18124 else
18125 tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x;
18126 }
18127 else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost))
18128 {
18129 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
18130 }
18131
18132 // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows)
18133 if (root_payload->DockNodeAsHost)
18134 IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size);
18135 ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL;
18136 const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1;
18137 for (int payload_n = 0; payload_n < payload_count; payload_n++)
18138 {
18139 // DockNode's TabBar may have non-window Tabs manually appended by user
18140 ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload;
18141 if (tab_bar_with_payload && payload_window == NULL)
18142 continue;
18143 if (!DockNodeIsDropAllowedOne(payload_window, host_window))
18144 continue;
18145
18146 // Calculate the tab bounding box for each payload window
18147 ImVec2 tab_size = TabItemCalcSize(payload_window);
18148 ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y);
18149 tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x;
18150 const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]);
18151 const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabActive]);
18152 PushStyleColor(ImGuiCol_Text, overlay_col_text);
18153 for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
18154 {
18155 ImGuiTabItemFlags tab_flags = (payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0;
18156 if (!tab_bar_rect.Contains(tab_bb))
18157 overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max);
18158 TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs);
18159 TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL);
18160 if (!tab_bar_rect.Contains(tab_bb))
18161 overlay_draw_lists[overlay_n]->PopClipRect();
18162 }
18163 PopStyleColor();
18164 }
18165 }
18166
18167 // Display drop boxes
18168 const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding);
18169 for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++)
18170 {
18171 if (!data->DropRectsDraw[dir + 1].IsInverted())
18172 {
18173 ImRect draw_r = data->DropRectsDraw[dir + 1];
18174 ImRect draw_r_in = draw_r;
18175 draw_r_in.Expand(-2.0f);
18176 ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop;
18177 for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++)
18178 {
18179 ImVec2 center = ImFloor(draw_r_in.GetCenter());
18180 overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding);
18181 overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding);
18182 if (dir == ImGuiDir_Left || dir == ImGuiDir_Right)
18183 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);
18184 if (dir == ImGuiDir_Up || dir == ImGuiDir_Down)
18185 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);
18186 }
18187 }
18188
18189 // Stop after ImGuiDir_None
18190 if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoDockingSplit)) || g.IO.ConfigDockingNoSplit)
18191 return;
18192 }
18193}
18194
18195//-----------------------------------------------------------------------------
18196// Docking: ImGuiDockNode Tree manipulation functions
18197//-----------------------------------------------------------------------------
18198// - DockNodeTreeSplit()
18199// - DockNodeTreeMerge()
18200// - DockNodeTreeUpdatePosSize()
18201// - DockNodeTreeUpdateSplitterFindTouchingNode()
18202// - DockNodeTreeUpdateSplitter()
18203// - DockNodeTreeFindFallbackLeafNode()
18204// - DockNodeTreeFindNodeByPos()
18205//-----------------------------------------------------------------------------
18206
18207void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node)
18208{
18209 ImGuiContext& g = *GImGui;
18210 IM_ASSERT(split_axis != ImGuiAxis_None);
18211
18212 ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0);
18213 child_0->ParentNode = parent_node;
18214
18215 ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0);
18216 child_1->ParentNode = parent_node;
18217
18218 ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1;
18219 DockNodeMoveChildNodes(child_inheritor, parent_node);
18220 parent_node->ChildNodes[0] = child_0;
18221 parent_node->ChildNodes[1] = child_1;
18222 parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow;
18223 parent_node->SplitAxis = split_axis;
18224 parent_node->VisibleWindow = NULL;
18225 parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode;
18226
18227 float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize);
18228 size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f);
18229 IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting.
18230 child_0->SizeRef = child_1->SizeRef = parent_node->Size;
18231 child_0->SizeRef[split_axis] = ImTrunc(size_avail * split_ratio);
18232 child_1->SizeRef[split_axis] = ImTrunc(size_avail - child_0->SizeRef[split_axis]);
18233
18234 DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node);
18235 DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID);
18236 DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node));
18237 DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size);
18238
18239 // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property)
18240 child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
18241 child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_;
18242 child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
18243 parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_;
18244 child_0->UpdateMergedFlags();
18245 child_1->UpdateMergedFlags();
18246 parent_node->UpdateMergedFlags();
18247 if (child_inheritor->IsCentralNode())
18248 DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor;
18249}
18250
18251void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child)
18252{
18253 // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL.
18254 ImGuiContext& g = *GImGui;
18255 ImGuiDockNode* child_0 = parent_node->ChildNodes[0];
18256 ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
18257 IM_ASSERT(child_0 || child_1);
18258 IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
18259 if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0))
18260 {
18261 IM_ASSERT(parent_node->TabBar == NULL);
18262 IM_ASSERT(parent_node->Windows.Size == 0);
18263 }
18264 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);
18265
18266 ImVec2 backup_last_explicit_size = parent_node->SizeRef;
18267 DockNodeMoveChildNodes(parent_node, merge_lead_child);
18268 if (child_0)
18269 {
18270 DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
18271 DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID);
18272 }
18273 if (child_1)
18274 {
18275 DockNodeMoveWindows(parent_node, child_1);
18276 DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID);
18277 }
18278 DockNodeApplyPosSizeToWindows(parent_node);
18279 parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto;
18280 parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
18281 parent_node->SizeRef = backup_last_explicit_size;
18282
18283 // Flags transfer
18284 parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag
18285 parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
18286 parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_;
18287 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
18288 parent_node->UpdateMergedFlags();
18289
18290 if (child_0)
18291 {
18292 ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL);
18293 IM_DELETE(child_0);
18294 }
18295 if (child_1)
18296 {
18297 ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL);
18298 IM_DELETE(child_1);
18299 }
18300}
18301
18302// Update Pos/Size for a node hierarchy (don't affect child Windows yet)
18303// (Depth-first, Pre-Order)
18304void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node)
18305{
18306 // During the regular dock node update we write to all nodes.
18307 // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away.
18308 ImGuiContext& g = *GImGui;
18309 const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node;
18310 if (write_to_node)
18311 {
18312 node->Pos = pos;
18313 node->Size = size;
18314 }
18315
18316 if (node->IsLeafNode())
18317 return;
18318
18319 ImGuiDockNode* child_0 = node->ChildNodes[0];
18320 ImGuiDockNode* child_1 = node->ChildNodes[1];
18321 ImVec2 child_0_pos = pos, child_1_pos = pos;
18322 ImVec2 child_0_size = size, child_1_size = size;
18323
18324 const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0));
18325 const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1));
18326 const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node;
18327 const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node;
18328
18329 if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible)
18330 {
18331 const float spacing = g.Style.DockingSeparatorSize;
18332 const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
18333 const float size_avail = ImMax(size[axis] - spacing, 0.0f);
18334
18335 // Size allocation policy
18336 // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows.
18337 const float size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f);
18338
18339 // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing.
18340 // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc()
18341 // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce
18342
18343 // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge)
18344 if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce)
18345 {
18346 child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]);
18347 child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
18348 IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
18349 }
18350 else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce)
18351 {
18352 child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]);
18353 child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]);
18354 IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
18355 }
18356 else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)
18357 {
18358 // FIXME-DOCK: We cannot honor the requested size, so apply ratio.
18359 // Currently this path will only be taken if code programmatically sets WantLockSizeOnce
18360 float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]);
18361 child_0_size[axis] = child_0->SizeRef[axis] = ImTrunc(size_avail * split_ratio);
18362 child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]);
18363 IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f);
18364 }
18365
18366 // 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
18367 else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild)
18368 {
18369 child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]);
18370 child_1_size[axis] = (size_avail - child_0_size[axis]);
18371 }
18372 else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild)
18373 {
18374 child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]);
18375 child_0_size[axis] = (size_avail - child_1_size[axis]);
18376 }
18377 else
18378 {
18379 // 4) Otherwise distribute according to the relative ratio of each SizeRef value
18380 float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]);
18381 child_0_size[axis] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5f));
18382 child_1_size[axis] = (size_avail - child_0_size[axis]);
18383 }
18384
18385 child_1_pos[axis] += spacing + child_0_size[axis];
18386 }
18387
18388 if (only_write_to_single_node == NULL)
18389 child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false;
18390
18391 const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible;
18392 const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible;
18393 if (child_0_recurse)
18394 DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size);
18395 if (child_1_recurse)
18396 DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size);
18397}
18398
18399static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector<ImGuiDockNode*>* touching_nodes)
18400{
18401 if (node->IsLeafNode())
18402 {
18403 touching_nodes->push_back(node);
18404 return;
18405 }
18406 if (node->ChildNodes[0]->IsVisible)
18407 if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible)
18408 DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes);
18409 if (node->ChildNodes[1]->IsVisible)
18410 if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible)
18411 DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes);
18412}
18413
18414// (Depth-First, Pre-Order)
18415void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node)
18416{
18417 if (node->IsLeafNode())
18418 return;
18419
18420 ImGuiContext& g = *GImGui;
18421
18422 ImGuiDockNode* child_0 = node->ChildNodes[0];
18423 ImGuiDockNode* child_1 = node->ChildNodes[1];
18424 if (child_0->IsVisible && child_1->IsVisible)
18425 {
18426 // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally)
18427 const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis;
18428 IM_ASSERT(axis != ImGuiAxis_None);
18429 ImRect bb;
18430 bb.Min = child_0->Pos;
18431 bb.Max = child_1->Pos;
18432 bb.Min[axis] += child_0->Size[axis];
18433 bb.Max[axis ^ 1] += child_1->Size[axis ^ 1];
18434 //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255));
18435
18436 const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs
18437 const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY;
18438 if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag))
18439 {
18440 ImGuiWindow* window = g.CurrentWindow;
18441 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding);
18442 }
18443 else
18444 {
18445 //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node.
18446 //bb.Max[axis] -= 1;
18447 PushID(node->ID);
18448
18449 // Find resizing limits by gathering list of nodes that are touching the splitter line.
18450 ImVector<ImGuiDockNode*> touching_nodes[2];
18451 float min_size = g.Style.WindowMinSize[axis];
18452 float resize_limits[2];
18453 resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size;
18454 resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size;
18455
18456 ImGuiID splitter_id = GetID("##Splitter");
18457 if (g.ActiveId == splitter_id) // Only process when splitter is active
18458 {
18459 DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]);
18460 DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]);
18461 for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++)
18462 resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size);
18463 for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++)
18464 resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size);
18465
18466 // [DEBUG] Render touching nodes & limits
18467 /*
18468 ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
18469 for (int n = 0; n < 2; n++)
18470 {
18471 for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++)
18472 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));
18473 if (axis == ImGuiAxis_X)
18474 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);
18475 else
18476 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);
18477 }
18478 */
18479 }
18480
18481 // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters
18482 float cur_size_0 = child_0->Size[axis];
18483 float cur_size_1 = child_1->Size[axis];
18484 float min_size_0 = resize_limits[0] - child_0->Pos[axis];
18485 float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1];
18486 ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg);
18487 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))
18488 {
18489 if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0)
18490 {
18491 child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0;
18492 child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis];
18493 child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1;
18494
18495 // Lock the size of every node that is a sibling of the node we are touching
18496 // This might be less desirable if we can merge sibling of a same axis into the same parental level.
18497 for (int side_n = 0; side_n < 2; side_n++)
18498 for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++)
18499 {
18500 ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n];
18501 //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
18502 //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255));
18503 while (touching_node->ParentNode != node)
18504 {
18505 if (touching_node->ParentNode->SplitAxis == axis)
18506 {
18507 // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize().
18508 ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n];
18509 node_to_preserve->WantLockSizeOnce = true;
18510 //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255));
18511 //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100));
18512 }
18513 touching_node = touching_node->ParentNode;
18514 }
18515 }
18516
18517 DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size);
18518 DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size);
18519 MarkIniSettingsDirty();
18520 }
18521 }
18522 PopID();
18523 }
18524 }
18525
18526 if (child_0->IsVisible)
18527 DockNodeTreeUpdateSplitter(child_0);
18528 if (child_1->IsVisible)
18529 DockNodeTreeUpdateSplitter(child_1);
18530}
18531
18532ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node)
18533{
18534 if (node->IsLeafNode())
18535 return node;
18536 if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0]))
18537 return leaf_node;
18538 if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1]))
18539 return leaf_node;
18540 return NULL;
18541}
18542
18543ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos)
18544{
18545 if (!node->IsVisible)
18546 return NULL;
18547
18548 const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE?
18549 ImRect r(node->Pos, node->Pos + node->Size);
18550 r.Expand(dock_spacing * 0.5f);
18551 bool inside = r.Contains(pos);
18552 if (!inside)
18553 return NULL;
18554
18555 if (node->IsLeafNode())
18556 return node;
18557 if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos))
18558 return hovered_node;
18559 if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos))
18560 return hovered_node;
18561
18562 // This means we are hovering over the splitter/spacing of a parent node
18563 return node;
18564}
18565
18566//-----------------------------------------------------------------------------
18567// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport)
18568//-----------------------------------------------------------------------------
18569// - SetWindowDock() [Internal]
18570// - DockSpace()
18571// - DockSpaceOverViewport()
18572//-----------------------------------------------------------------------------
18573
18574// [Internal] Called via SetNextWindowDockID()
18575void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond)
18576{
18577 // Test condition (NB: bit 0 is always true) and clear flags for next time
18578 if (cond && (window->SetWindowDockAllowFlags & cond) == 0)
18579 return;
18580 window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
18581
18582 if (window->DockId == dock_id)
18583 return;
18584
18585 // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot
18586 ImGuiContext& g = *GImGui;
18587 if (ImGuiDockNode* new_node = DockContextFindNodeByID(&g, dock_id))
18588 if (new_node->IsSplitNode())
18589 {
18590 // Policy: Find central node or latest focused node. We first move back to our root node.
18591 new_node = DockNodeGetRootNode(new_node);
18592 if (new_node->CentralNode)
18593 {
18594 IM_ASSERT(new_node->CentralNode->IsCentralNode());
18595 dock_id = new_node->CentralNode->ID;
18596 }
18597 else
18598 {
18599 dock_id = new_node->LastFocusedNodeId;
18600 }
18601 }
18602
18603 if (window->DockId == dock_id)
18604 return;
18605
18606 if (window->DockNode)
18607 DockNodeRemoveWindow(window->DockNode, window, 0);
18608 window->DockId = dock_id;
18609}
18610
18611// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default.
18612// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors.
18613// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app.
18614// When ImGuiDockNodeFlags_KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location).
18615ImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class)
18616{
18617 ImGuiContext& g = *GImGui;
18618 ImGuiWindow* window = GetCurrentWindowRead();
18619 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
18620 return 0;
18621
18622 // Early out if parent window is hidden/collapsed
18623 // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960.
18624 // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true.
18625 if (window->SkipItems)
18626 flags |= ImGuiDockNodeFlags_KeepAliveOnly;
18627 if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0)
18628 window = GetCurrentWindow(); // call to set window->WriteAccessed = true;
18629
18630 IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0);
18631 IM_ASSERT(dockspace_id != 0);
18632 ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id);
18633 if (node == NULL)
18634 {
18635 IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X created\n", dockspace_id);
18636 node = DockContextAddNode(&g, dockspace_id);
18637 node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode);
18638 }
18639 if (window_class && window_class->ClassId != node->WindowClass.ClassId)
18640 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);
18641 node->SharedFlags = flags;
18642 node->WindowClass = window_class ? *window_class : ImGuiWindowClass();
18643
18644 // When a DockSpace transitioned form implicit to explicit this may be called a second time
18645 // 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.
18646 if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly))
18647 {
18648 IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID");
18649 node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
18650 return dockspace_id;
18651 }
18652 node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace);
18653
18654 // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible
18655 if (flags & ImGuiDockNodeFlags_KeepAliveOnly)
18656 {
18657 node->LastFrameAlive = g.FrameCount;
18658 return dockspace_id;
18659 }
18660
18661 const ImVec2 content_avail = GetContentRegionAvail();
18662 ImVec2 size = ImTrunc(size_arg);
18663 if (size.x <= 0.0f)
18664 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
18665 if (size.y <= 0.0f)
18666 size.y = ImMax(content_avail.y + size.y, 4.0f);
18667 IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
18668
18669 node->Pos = window->DC.CursorPos;
18670 node->Size = node->SizeRef = size;
18671 SetNextWindowPos(node->Pos);
18672 SetNextWindowSize(node->Size);
18673 g.NextWindowData.PosUndock = false;
18674
18675 // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window?
18676 // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented)
18677 ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost;
18678 window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
18679 window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
18680 window_flags |= ImGuiWindowFlags_NoBackground;
18681
18682 char title[256];
18683 ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, dockspace_id);
18684
18685 PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
18686 Begin(title, NULL, window_flags);
18687 PopStyleVar();
18688
18689 ImGuiWindow* host_window = g.CurrentWindow;
18690 DockNodeSetupHostWindow(node, host_window);
18691 host_window->ChildId = window->GetID(title);
18692 node->OnlyNodeWithWindows = NULL;
18693
18694 IM_ASSERT(node->IsRootNode());
18695
18696 // We need to handle the rare case were a central node is missing.
18697 // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace.
18698 // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split.
18699 // 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.
18700 // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property,
18701 // as it doesn't make sense for an empty dockspace to not have this property.
18702 if (node->IsLeafNode() && !node->IsCentralNode())
18703 node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
18704
18705 // Update the node
18706 DockNodeUpdate(node);
18707
18708 End();
18709
18710 ImRect bb(node->Pos, node->Pos + size);
18711 ItemSize(size);
18712 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?)
18713 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && IsWindowChildOf(g.HoveredWindow, host_window, false, true)) // To fullfill IsItemHovered(), similar to EndChild()
18714 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
18715
18716 return dockspace_id;
18717}
18718
18719// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode!
18720// The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar().
18721// 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.
18722// 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.
18723ImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class)
18724{
18725 if (viewport == NULL)
18726 viewport = GetMainViewport();
18727
18728 // Submit a window filling the entire viewport
18729 SetNextWindowPos(viewport->WorkPos);
18730 SetNextWindowSize(viewport->WorkSize);
18731 SetNextWindowViewport(viewport->ID);
18732
18733 ImGuiWindowFlags host_window_flags = 0;
18734 host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
18735 host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
18736 if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
18737 host_window_flags |= ImGuiWindowFlags_NoBackground;
18738
18739 char label[32];
18740 ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", viewport->ID);
18741
18742 PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
18743 PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
18744 PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
18745 Begin(label, NULL, host_window_flags);
18746 PopStyleVar(3);
18747
18748 // Submit the dockspace
18749 if (dockspace_id == 0)
18750 dockspace_id = GetID("DockSpace");
18751 DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class);
18752
18753 End();
18754
18755 return dockspace_id;
18756}
18757
18758//-----------------------------------------------------------------------------
18759// Docking: Builder Functions
18760//-----------------------------------------------------------------------------
18761// Very early end-user API to manipulate dock nodes.
18762// Only available in imgui_internal.h. Expect this API to change/break!
18763// It is expected that those functions are all called _before_ the dockspace node submission.
18764//-----------------------------------------------------------------------------
18765// - DockBuilderDockWindow()
18766// - DockBuilderGetNode()
18767// - DockBuilderSetNodePos()
18768// - DockBuilderSetNodeSize()
18769// - DockBuilderAddNode()
18770// - DockBuilderRemoveNode()
18771// - DockBuilderRemoveNodeChildNodes()
18772// - DockBuilderRemoveNodeDockedWindows()
18773// - DockBuilderSplitNode()
18774// - DockBuilderCopyNodeRec()
18775// - DockBuilderCopyNode()
18776// - DockBuilderCopyWindowSettings()
18777// - DockBuilderCopyDockSpace()
18778// - DockBuilderFinish()
18779//-----------------------------------------------------------------------------
18780
18781void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id)
18782{
18783 // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1)
18784 ImGuiContext& g = *GImGui; IM_UNUSED(g);
18785 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderDockWindow '%s' to node 0x%08X\n", window_name, node_id);
18786 ImGuiID window_id = ImHashStr(window_name);
18787 if (ImGuiWindow* window = FindWindowByID(window_id))
18788 {
18789 // Apply to created window
18790 ImGuiID prev_node_id = window->DockId;
18791 SetWindowDock(window, node_id, ImGuiCond_Always);
18792 if (window->DockId != prev_node_id)
18793 window->DockOrder = -1;
18794 }
18795 else
18796 {
18797 // Apply to settings
18798 ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id);
18799 if (settings == NULL)
18800 settings = CreateNewWindowSettings(window_name);
18801 if (settings->DockId != node_id)
18802 settings->DockOrder = -1;
18803 settings->DockId = node_id;
18804 }
18805}
18806
18807ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id)
18808{
18809 ImGuiContext& g = *GImGui;
18810 return DockContextFindNodeByID(&g, node_id);
18811}
18812
18813void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos)
18814{
18815 ImGuiContext& g = *GImGui;
18816 ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
18817 if (node == NULL)
18818 return;
18819 node->Pos = pos;
18820 node->AuthorityForPos = ImGuiDataAuthority_DockNode;
18821}
18822
18823void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size)
18824{
18825 ImGuiContext& g = *GImGui;
18826 ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
18827 if (node == NULL)
18828 return;
18829 IM_ASSERT(size.x > 0.0f && size.y > 0.0f);
18830 node->Size = node->SizeRef = size;
18831 node->AuthorityForSize = ImGuiDataAuthority_DockNode;
18832}
18833
18834// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node!
18835// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node.
18836// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary.
18837// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand!
18838// For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect.
18839// - Use (id == 0) to let the system allocate a node identifier.
18840// - Existing node with a same id will be removed.
18841ImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags)
18842{
18843 ImGuiContext& g = *GImGui; IM_UNUSED(g);
18844 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderAddNode 0x%08X flags=%08X\n", node_id, flags);
18845
18846 if (node_id != 0)
18847 DockBuilderRemoveNode(node_id);
18848
18849 ImGuiDockNode* node = NULL;
18850 if (flags & ImGuiDockNodeFlags_DockSpace)
18851 {
18852 DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly);
18853 node = DockContextFindNodeByID(&g, node_id);
18854 }
18855 else
18856 {
18857 node = DockContextAddNode(&g, node_id);
18858 node->SetLocalFlags(flags);
18859 }
18860 node->LastFrameAlive = g.FrameCount; // Set this otherwise BeginDocked will undock during the same frame.
18861 return node->ID;
18862}
18863
18864void ImGui::DockBuilderRemoveNode(ImGuiID node_id)
18865{
18866 ImGuiContext& g = *GImGui; IM_UNUSED(g);
18867 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderRemoveNode 0x%08X\n", node_id);
18868
18869 ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id);
18870 if (node == NULL)
18871 return;
18872 DockBuilderRemoveNodeDockedWindows(node_id, true);
18873 DockBuilderRemoveNodeChildNodes(node_id);
18874 // Node may have moved or deleted if e.g. any merge happened
18875 node = DockContextFindNodeByID(&g, node_id);
18876 if (node == NULL)
18877 return;
18878 if (node->IsCentralNode() && node->ParentNode)
18879 node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode);
18880 DockContextRemoveNode(&g, node, true);
18881}
18882
18883// root_id = 0 to remove all, root_id != 0 to remove child of given node.
18884void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id)
18885{
18886 ImGuiContext& g = *GImGui;
18887 ImGuiDockContext* dc = &g.DockContext;
18888
18889 ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(&g, root_id) : NULL;
18890 if (root_id && root_node == NULL)
18891 return;
18892 bool has_central_node = false;
18893
18894 ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto;
18895 ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto;
18896
18897 // Process active windows
18898 ImVector<ImGuiDockNode*> nodes_to_remove;
18899 for (int n = 0; n < dc->Nodes.Data.Size; n++)
18900 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
18901 {
18902 bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
18903 if (want_removal)
18904 {
18905 if (node->IsCentralNode())
18906 has_central_node = true;
18907 if (root_id != 0)
18908 DockContextQueueNotifyRemovedNode(&g, node);
18909 if (root_node)
18910 {
18911 DockNodeMoveWindows(root_node, node);
18912 DockSettingsRenameNodeReferences(node->ID, root_node->ID);
18913 }
18914 nodes_to_remove.push_back(node);
18915 }
18916 }
18917
18918 // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge)
18919 // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead)
18920 if (root_node)
18921 {
18922 root_node->AuthorityForPos = backup_root_node_authority_for_pos;
18923 root_node->AuthorityForSize = backup_root_node_authority_for_size;
18924 }
18925
18926 // Apply to settings
18927 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
18928 if (ImGuiID window_settings_dock_id = settings->DockId)
18929 for (int n = 0; n < nodes_to_remove.Size; n++)
18930 if (nodes_to_remove[n]->ID == window_settings_dock_id)
18931 {
18932 settings->DockId = root_id;
18933 break;
18934 }
18935
18936 // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
18937 if (nodes_to_remove.Size > 1)
18938 ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
18939 for (int n = 0; n < nodes_to_remove.Size; n++)
18940 DockContextRemoveNode(&g, nodes_to_remove[n], false);
18941
18942 if (root_id == 0)
18943 {
18944 dc->Nodes.Clear();
18945 dc->Requests.clear();
18946 }
18947 else if (has_central_node)
18948 {
18949 root_node->CentralNode = root_node;
18950 root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode);
18951 }
18952}
18953
18954void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs)
18955{
18956 // Clear references in settings
18957 ImGuiContext& g = *GImGui;
18958 if (clear_settings_refs)
18959 {
18960 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
18961 {
18962 bool want_removal = (root_id == 0) || (settings->DockId == root_id);
18963 if (!want_removal && settings->DockId != 0)
18964 if (ImGuiDockNode* node = DockContextFindNodeByID(&g, settings->DockId))
18965 if (DockNodeGetRootNode(node)->ID == root_id)
18966 want_removal = true;
18967 if (want_removal)
18968 settings->DockId = 0;
18969 }
18970 }
18971
18972 // Clear references in windows
18973 for (int n = 0; n < g.Windows.Size; n++)
18974 {
18975 ImGuiWindow* window = g.Windows[n];
18976 bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
18977 if (want_removal)
18978 {
18979 const ImGuiID backup_dock_id = window->DockId;
18980 IM_UNUSED(backup_dock_id);
18981 DockContextProcessUndockWindow(&g, window, clear_settings_refs);
18982 if (!clear_settings_refs)
18983 IM_ASSERT(window->DockId == backup_dock_id);
18984 }
18985 }
18986}
18987
18988// 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.
18989// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set.
18990// FIXME-DOCK: We are not exposing nor using split_outer.
18991ImGuiID 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)
18992{
18993 ImGuiContext& g = *GImGui;
18994 IM_ASSERT(split_dir != ImGuiDir_None);
18995 IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderSplitNode: node 0x%08X, split_dir %d\n", id, split_dir);
18996
18997 ImGuiDockNode* node = DockContextFindNodeByID(&g, id);
18998 if (node == NULL)
18999 {
19000 IM_ASSERT(node != NULL);
19001 return 0;
19002 }
19003
19004 IM_ASSERT(!node->IsSplitNode()); // Assert if already Split
19005
19006 ImGuiDockRequest req;
19008 req.DockTargetWindow = NULL;
19009 req.DockTargetNode = node;
19010 req.DockPayload = NULL;
19011 req.DockSplitDir = split_dir;
19012 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);
19013 req.DockSplitOuter = false;
19014 DockContextProcessDock(&g, &req);
19015
19016 ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
19017 ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
19018 if (out_id_at_dir)
19019 *out_id_at_dir = id_at_dir;
19020 if (out_id_at_opposite_dir)
19021 *out_id_at_opposite_dir = id_at_opposite_dir;
19022 return id_at_dir;
19023}
19024
19025static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector<ImGuiID>* out_node_remap_pairs)
19026{
19027 ImGuiContext& g = *GImGui;
19028 ImGuiDockNode* dst_node = ImGui::DockContextAddNode(&g, dst_node_id_if_known);
19029 dst_node->SharedFlags = src_node->SharedFlags;
19030 dst_node->LocalFlags = src_node->LocalFlags;
19031 dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None;
19032 dst_node->Pos = src_node->Pos;
19033 dst_node->Size = src_node->Size;
19034 dst_node->SizeRef = src_node->SizeRef;
19035 dst_node->SplitAxis = src_node->SplitAxis;
19036 dst_node->UpdateMergedFlags();
19037
19038 out_node_remap_pairs->push_back(src_node->ID);
19039 out_node_remap_pairs->push_back(dst_node->ID);
19040
19041 for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++)
19042 if (src_node->ChildNodes[child_n])
19043 {
19044 dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs);
19045 dst_node->ChildNodes[child_n]->ParentNode = dst_node;
19046 }
19047
19048 IMGUI_DEBUG_LOG_DOCKING("[docking] Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0);
19049 return dst_node;
19050}
19051
19052void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector<ImGuiID>* out_node_remap_pairs)
19053{
19054 ImGuiContext& g = *GImGui;
19055 IM_ASSERT(src_node_id != 0);
19056 IM_ASSERT(dst_node_id != 0);
19057 IM_ASSERT(out_node_remap_pairs != NULL);
19058
19059 DockBuilderRemoveNode(dst_node_id);
19060
19061 ImGuiDockNode* src_node = DockContextFindNodeByID(&g, src_node_id);
19062 IM_ASSERT(src_node != NULL);
19063
19064 out_node_remap_pairs->clear();
19065 DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs);
19066
19067 IM_ASSERT((out_node_remap_pairs->Size % 2) == 0);
19068}
19069
19070void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name)
19071{
19072 ImGuiWindow* src_window = FindWindowByName(src_name);
19073 if (src_window == NULL)
19074 return;
19075 if (ImGuiWindow* dst_window = FindWindowByName(dst_name))
19076 {
19077 dst_window->Pos = src_window->Pos;
19078 dst_window->Size = src_window->Size;
19079 dst_window->SizeFull = src_window->SizeFull;
19080 dst_window->Collapsed = src_window->Collapsed;
19081 }
19082 else
19083 {
19084 ImGuiWindowSettings* dst_settings = FindWindowSettingsByID(ImHashStr(dst_name));
19085 if (!dst_settings)
19086 dst_settings = CreateNewWindowSettings(dst_name);
19087 ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos);
19088 if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID)
19089 {
19090 dst_settings->ViewportPos = window_pos_2ih;
19091 dst_settings->ViewportId = src_window->ViewportId;
19092 dst_settings->Pos = ImVec2ih(0, 0);
19093 }
19094 else
19095 {
19096 dst_settings->Pos = window_pos_2ih;
19097 }
19098 dst_settings->Size = ImVec2ih(src_window->SizeFull);
19099 dst_settings->Collapsed = src_window->Collapsed;
19100 }
19101}
19102
19103// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed.
19104void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector<const char*>* in_window_remap_pairs)
19105{
19106 ImGuiContext& g = *GImGui;
19107 IM_ASSERT(src_dockspace_id != 0);
19108 IM_ASSERT(dst_dockspace_id != 0);
19109 IM_ASSERT(in_window_remap_pairs != NULL);
19110 IM_ASSERT((in_window_remap_pairs->Size % 2) == 0);
19111
19112 // Duplicate entire dock
19113 // 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,
19114 // whereas we could attempt to at least keep them together in a new, same floating node.
19115 ImVector<ImGuiID> node_remap_pairs;
19116 DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs);
19117
19118 // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes
19119 // (The windows associated to src_dockspace_id are staying in place)
19120 ImVector<ImGuiID> src_windows;
19121 for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2)
19122 {
19123 const char* src_window_name = (*in_window_remap_pairs)[remap_window_n];
19124 const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1];
19125 ImGuiID src_window_id = ImHashStr(src_window_name);
19126 src_windows.push_back(src_window_id);
19127
19128 // Search in the remapping tables
19129 ImGuiID src_dock_id = 0;
19130 if (ImGuiWindow* src_window = FindWindowByID(src_window_id))
19131 src_dock_id = src_window->DockId;
19132 else if (ImGuiWindowSettings* src_window_settings = FindWindowSettingsByID(src_window_id))
19133 src_dock_id = src_window_settings->DockId;
19134 ImGuiID dst_dock_id = 0;
19135 for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
19136 if (node_remap_pairs[dock_remap_n] == src_dock_id)
19137 {
19138 dst_dock_id = node_remap_pairs[dock_remap_n + 1];
19139 //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear
19140 break;
19141 }
19142
19143 if (dst_dock_id != 0)
19144 {
19145 // Docked windows gets redocked into the new node hierarchy.
19146 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);
19147 DockBuilderDockWindow(dst_window_name, dst_dock_id);
19148 }
19149 else
19150 {
19151 // Floating windows gets their settings transferred (regardless of whether the new window already exist or not)
19152 // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together?
19153 IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name);
19154 DockBuilderCopyWindowSettings(src_window_name, dst_window_name);
19155 }
19156 }
19157
19158 // Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list.
19159 // Find those windows and move to them to the cloned dock node. This may be optional?
19160 // Dock those are a second step as undocking would invalidate source dock nodes.
19161 struct DockRemainingWindowTask { ImGuiWindow* Window; ImGuiID DockId; DockRemainingWindowTask(ImGuiWindow* window, ImGuiID dock_id) { Window = window; DockId = dock_id; } };
19162 ImVector<DockRemainingWindowTask> dock_remaining_windows;
19163 for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2)
19164 if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n])
19165 {
19166 ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1];
19167 ImGuiDockNode* node = DockBuilderGetNode(src_dock_id);
19168 for (int window_n = 0; window_n < node->Windows.Size; window_n++)
19169 {
19170 ImGuiWindow* window = node->Windows[window_n];
19171 if (src_windows.contains(window->ID))
19172 continue;
19173
19174 // Docked windows gets redocked into the new node hierarchy.
19175 IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id);
19176 dock_remaining_windows.push_back(DockRemainingWindowTask(window, dst_dock_id));
19177 }
19178 }
19179 for (const DockRemainingWindowTask& task : dock_remaining_windows)
19180 DockBuilderDockWindow(task.Window->Name, task.DockId);
19181}
19182
19183// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node.
19184void ImGui::DockBuilderFinish(ImGuiID root_id)
19185{
19186 ImGuiContext& g = *GImGui;
19187 //DockContextRebuild(&g);
19188 DockContextBuildAddWindowsToNodes(&g, root_id);
19189}
19190
19191//-----------------------------------------------------------------------------
19192// Docking: Begin/End Support Functions (called from Begin/End)
19193//-----------------------------------------------------------------------------
19194// - GetWindowAlwaysWantOwnTabBar()
19195// - DockContextBindNodeToWindow()
19196// - BeginDocked()
19197// - BeginDockableDragDropSource()
19198// - BeginDockableDragDropTarget()
19199//-----------------------------------------------------------------------------
19200
19201bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window)
19202{
19203 ImGuiContext& g = *GImGui;
19204 if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar)
19205 if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0)
19206 if (!window->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise
19207 return true;
19208 return false;
19209}
19210
19211static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window)
19212{
19213 ImGuiContext& g = *ctx;
19214 ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId);
19215 IM_ASSERT(window->DockNode == NULL);
19216
19217 // We should not be docking into a split node (SetWindowDock should avoid this)
19218 if (node && node->IsSplitNode())
19219 {
19220 DockContextProcessUndockWindow(ctx, window);
19221 return NULL;
19222 }
19223
19224 // Create node
19225 if (node == NULL)
19226 {
19227 node = DockContextAddNode(ctx, window->DockId);
19228 node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window;
19229 node->LastFrameAlive = g.FrameCount;
19230 }
19231
19232 // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet,
19233 // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node).
19234 // 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.
19235 // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame.
19236 if (!node->IsVisible)
19237 {
19238 ImGuiDockNode* ancestor_node = node;
19239 while (!ancestor_node->IsVisible && ancestor_node->ParentNode)
19240 ancestor_node = ancestor_node->ParentNode;
19241 IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f);
19242 DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node));
19243 DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node);
19244 }
19245
19246 // Add window to node
19247 bool node_was_visible = node->IsVisible;
19248 DockNodeAddWindow(node, window, true);
19249 node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see)
19250 IM_ASSERT(node == window->DockNode);
19251 return node;
19252}
19253
19254static void StoreDockStyleForWindow(ImGuiWindow* window)
19255{
19256 ImGuiContext& g = *GImGui;
19257 for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++)
19258 window->DockStyle.Colors[color_n] = ImGui::ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]);
19259}
19260
19261void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
19262{
19263 ImGuiContext& g = *GImGui;
19264
19265 // Clear fields ahead so most early-out paths don't have to do it
19266 window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false;
19267
19268 const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window);
19269 if (auto_dock_node)
19270 {
19271 if (window->DockId == 0)
19272 {
19273 IM_ASSERT(window->DockNode == NULL);
19274 window->DockId = DockContextGenNodeID(&g);
19275 }
19276 }
19277 else
19278 {
19279 // Calling SetNextWindowPos() undock windows by default (by setting PosUndock)
19280 bool want_undock = false;
19281 want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0;
19282 want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock;
19283 if (want_undock)
19284 {
19285 DockContextProcessUndockWindow(&g, window);
19286 return;
19287 }
19288 }
19289
19290 // Bind to our dock node
19291 ImGuiDockNode* node = window->DockNode;
19292 if (node != NULL)
19293 IM_ASSERT(window->DockId == node->ID);
19294 if (window->DockId != 0 && node == NULL)
19295 {
19296 node = DockContextBindNodeToWindow(&g, window);
19297 if (node == NULL)
19298 return;
19299 }
19300
19301#if 0
19302 // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
19303 if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
19304 {
19305 DockContextProcessUndockWindow(ctx, window);
19306 return;
19307 }
19308#endif
19309
19310 // Undock if our dockspace node disappeared
19311 // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly.
19312 if (node->LastFrameAlive < g.FrameCount)
19313 {
19314 // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking()
19315 ImGuiDockNode* root_node = DockNodeGetRootNode(node);
19316 if (root_node->LastFrameAlive < g.FrameCount)
19317 DockContextProcessUndockWindow(&g, window);
19318 else
19319 window->DockIsActive = true;
19320 return;
19321 }
19322
19323 // Store style overrides
19324 StoreDockStyleForWindow(window);
19325
19326 // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window,
19327 // and never create neither a host window neither a tab bar.
19328 // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test)
19329 if (node->HostWindow == NULL)
19330 {
19331 if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing)
19332 window->DockIsActive = true;
19333 if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window
19334 DockNodeHideWindowDuringHostWindowCreation(window);
19335 return;
19336 }
19337
19338 // We can have zero-sized nodes (e.g. children of a small-size dockspace)
19339 IM_ASSERT(node->HostWindow);
19340 IM_ASSERT(node->IsLeafNode());
19341 IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f);
19342 node->State = ImGuiDockNodeState_HostWindowVisible;
19343
19344 // Undock if we are submitted earlier than the host window
19345 if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext)
19346 {
19347 DockContextProcessUndockWindow(&g, window);
19348 return;
19349 }
19350
19351 // Position/Size window
19352 SetNextWindowPos(node->Pos);
19353 SetNextWindowSize(node->Size);
19354 g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos()
19355 window->DockIsActive = true;
19356 window->DockNodeIsVisible = true;
19357 window->DockTabIsVisible = false;
19358 if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly)
19359 return;
19360
19361 // When the window is selected we mark it as visible.
19362 if (node->VisibleWindow == window)
19363 window->DockTabIsVisible = true;
19364
19365 // Update window flag
19366 IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
19367 window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize;
19368 window->ChildFlags |= ImGuiChildFlags_AlwaysUseWindowPadding;
19369 if (node->IsHiddenTabBar() || node->IsNoTabBar())
19370 window->Flags |= ImGuiWindowFlags_NoTitleBar;
19371 else
19372 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!
19373
19374 // Save new dock order only if the window has been visible once already
19375 // This allows multiple windows to be created in the same frame and have their respective dock orders preserved.
19376 if (node->TabBar && window->WasActive)
19377 window->DockOrder = (short)DockNodeGetTabOrder(window);
19378
19379 if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL)
19380 *p_open = false;
19381
19382 // Update ChildId to allow returning from Child to Parent with Escape
19383 ImGuiWindow* parent_window = window->DockNode->HostWindow;
19384 window->ChildId = parent_window->GetID(window->Name);
19385}
19386
19387void ImGui::BeginDockableDragDropSource(ImGuiWindow* window)
19388{
19389 ImGuiContext& g = *GImGui;
19390 IM_ASSERT(g.ActiveId == window->MoveId);
19391 IM_ASSERT(g.MovingWindow == window);
19392 IM_ASSERT(g.CurrentWindow == window);
19393
19394 // 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking.
19395 if (g.IO.ConfigDockingWithShift != g.IO.KeyShift)
19396 {
19397 // When ConfigDockingWithShift is set, display a tooltip to increase UI affordance.
19398 // We cannot set for HoveredWindowUnderMovingWindow != NULL here, as it is only valid/useful when drag and drop is already active
19399 // (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function)
19400 IM_ASSERT(g.NextWindowData.Flags == 0);
19401 if (g.IO.ConfigDockingWithShift && g.MouseStationaryTimer >= 1.0f && g.ActiveId >= 1.0f)
19402 SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingHoldShiftToDock));
19403 return;
19404 }
19405
19406 g.LastItemData.ID = window->MoveId;
19407 window = window->RootWindowDockTree;
19408 IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
19409 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
19410 if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload))
19411 {
19412 SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window));
19413 EndDragDropSource();
19414 StoreDockStyleForWindow(window); // Store style overrides while dragging (even when not docked) because docking preview may need it.
19415 }
19416}
19417
19418void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window)
19419{
19420 ImGuiContext& g = *GImGui;
19421
19422 //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace
19423 IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0);
19424 if (!g.DragDropActive)
19425 return;
19426 //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
19427 if (!BeginDragDropTargetCustom(window->Rect(), window->ID))
19428 return;
19429
19430 // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering
19431 // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly)
19432 const ImGuiPayload* payload = &g.DragDropPayload;
19433 if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data))
19434 {
19435 EndDragDropTarget();
19436 return;
19437 }
19438
19439 ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data;
19440 if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect))
19441 {
19442 // Select target node
19443 // (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)
19444 bool dock_into_floating_window = false;
19445 ImGuiDockNode* node = NULL;
19446 if (window->DockNodeAsHost)
19447 {
19448 // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos().
19449 node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos);
19450
19451 // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active)
19452 // In this case we need to fallback into any leaf mode, possibly the central node.
19453 // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first.
19454 if (node && node->IsDockSpace() && node->IsRootNode())
19455 node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node);
19456 }
19457 else
19458 {
19459 if (window->DockNode)
19460 node = window->DockNode;
19461 else
19462 dock_into_floating_window = true; // Dock into a regular window
19463 }
19464
19465 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()));
19466 const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max);
19467
19468 // Preview docking request and find out split direction/ratio
19469 //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.
19470 const bool do_preview = payload->IsPreview() || payload->IsDelivery();
19471 if (do_preview && (node != NULL || dock_into_floating_window))
19472 {
19473 // 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.
19474 ImGuiDockPreviewData split_inner;
19475 ImGuiDockPreviewData split_outer;
19476 ImGuiDockPreviewData* split_data = &split_inner;
19477 if (node && (node->ParentNode || node->IsCentralNode() || !node->IsLeafNode()))
19478 if (ImGuiDockNode* root_node = DockNodeGetRootNode(node))
19479 {
19480 DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true);
19481 if (split_outer.IsSplitDirExplicit)
19482 split_data = &split_outer;
19483 }
19484 if (!node || node->IsLeafNode())
19485 DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false);
19486 if (split_data == &split_outer)
19487 split_inner.IsDropAllowed = false;
19488
19489 // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes
19490 DockNodePreviewDockRender(window, node, payload_window, &split_inner);
19491 DockNodePreviewDockRender(window, node, payload_window, &split_outer);
19492
19493 // Queue docking request
19494 if (split_data->IsDropAllowed && payload->IsDelivery())
19495 DockContextQueueDock(&g, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer);
19496 }
19497 }
19498 EndDragDropTarget();
19499}
19500
19501//-----------------------------------------------------------------------------
19502// Docking: Settings
19503//-----------------------------------------------------------------------------
19504// - DockSettingsRenameNodeReferences()
19505// - DockSettingsRemoveNodeReferences()
19506// - DockSettingsFindNodeSettings()
19507// - DockSettingsHandler_ApplyAll()
19508// - DockSettingsHandler_ReadOpen()
19509// - DockSettingsHandler_ReadLine()
19510// - DockSettingsHandler_DockNodeToSettings()
19511// - DockSettingsHandler_WriteAll()
19512//-----------------------------------------------------------------------------
19513
19514static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id)
19515{
19516 ImGuiContext& g = *GImGui;
19517 IMGUI_DEBUG_LOG_DOCKING("[docking] DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id);
19518 for (int window_n = 0; window_n < g.Windows.Size; window_n++)
19519 {
19520 ImGuiWindow* window = g.Windows[window_n];
19521 if (window->DockId == old_node_id && window->DockNode == NULL)
19522 window->DockId = new_node_id;
19523 }
19525 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
19526 if (settings->DockId == old_node_id)
19527 settings->DockId = new_node_id;
19528}
19529
19530// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
19531static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count)
19532{
19533 ImGuiContext& g = *GImGui;
19534 int found = 0;
19536 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
19537 for (int node_n = 0; node_n < node_ids_count; node_n++)
19538 if (settings->DockId == node_ids[node_n])
19539 {
19540 settings->DockId = 0;
19541 settings->DockOrder = -1;
19542 if (++found < node_ids_count)
19543 break;
19544 return;
19545 }
19546}
19547
19548static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id)
19549{
19550 // FIXME-OPT
19551 ImGuiDockContext* dc = &ctx->DockContext;
19552 for (int n = 0; n < dc->NodesSettings.Size; n++)
19553 if (dc->NodesSettings[n].ID == id)
19554 return &dc->NodesSettings[n];
19555 return NULL;
19556}
19557
19558// Clear settings data
19559static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
19560{
19561 ImGuiDockContext* dc = &ctx->DockContext;
19562 dc->NodesSettings.clear();
19563 DockContextClearNodes(ctx, 0, true);
19564}
19565
19566// Recreate nodes based on settings data
19567static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
19568{
19569 // Prune settings at boot time only
19570 ImGuiDockContext* dc = &ctx->DockContext;
19571 if (ctx->Windows.Size == 0)
19572 DockContextPruneUnusedSettingsNodes(ctx);
19573 DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size);
19574 DockContextBuildAddWindowsToNodes(ctx, 0);
19575}
19576
19577static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
19578{
19579 if (strcmp(name, "Data") != 0)
19580 return NULL;
19581 return (void*)1;
19582}
19583
19584static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line)
19585{
19586 char c = 0;
19587 int x = 0, y = 0;
19588 int r = 0;
19589
19590 // Parsing, e.g.
19591 // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 "
19592 // " DockNode ID=0x00000002 Parent=0x00000001 "
19593 // Important: this code expect currently fields in a fixed order.
19595 line = ImStrSkipBlank(line);
19596 if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); }
19597 else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; }
19598 else return;
19599 if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return;
19600 if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1) { line += r; if (node.ParentNodeId == 0) return; }
19601 if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; }
19602 if (node.ParentNodeId == 0)
19603 {
19604 if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return;
19605 if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return;
19606 }
19607 else
19608 {
19609 if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); }
19610 }
19611 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; }
19612 if (sscanf(line, " NoResize=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }
19613 if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; }
19614 if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; }
19615 if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; }
19616 if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; }
19617 if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; }
19618 if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; }
19619 if (node.ParentNodeId != 0)
19620 if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId))
19621 node.Depth = parent_settings->Depth + 1;
19622 ctx->DockContext.NodesSettings.push_back(node);
19623}
19624
19625static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth)
19626{
19627 ImGuiDockNodeSettings node_settings;
19628 IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
19629 node_settings.ID = node->ID;
19630 node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0;
19631 node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0;
19632 node_settings.SelectedTabId = node->SelectedTabId;
19633 node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None);
19634 node_settings.Depth = (char)depth;
19635 node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_);
19636 node_settings.Pos = ImVec2ih(node->Pos);
19637 node_settings.Size = ImVec2ih(node->Size);
19638 node_settings.SizeRef = ImVec2ih(node->SizeRef);
19639 dc->NodesSettings.push_back(node_settings);
19640 if (node->ChildNodes[0])
19641 DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1);
19642 if (node->ChildNodes[1])
19643 DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1);
19644}
19645
19646static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
19647{
19648 ImGuiContext& g = *ctx;
19649 ImGuiDockContext* dc = &ctx->DockContext;
19650 if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
19651 return;
19652
19653 // Gather settings data
19654 // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
19655 dc->NodesSettings.resize(0);
19656 dc->NodesSettings.reserve(dc->Nodes.Data.Size);
19657 for (int n = 0; n < dc->Nodes.Data.Size; n++)
19658 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
19659 if (node->IsRootNode())
19660 DockSettingsHandler_DockNodeToSettings(dc, node, 0);
19661
19662 int max_depth = 0;
19663 for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
19664 max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth);
19665
19666 // Write to text buffer
19667 buf->appendf("[%s][Data]\n", handler->TypeName);
19668 for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++)
19669 {
19670 const int line_start_pos = buf->size(); (void)line_start_pos;
19671 const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n];
19672 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
19673 buf->appendf(" ID=0x%08X", node_settings->ID);
19674 if (node_settings->ParentNodeId)
19675 {
19676 buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y);
19677 }
19678 else
19679 {
19680 if (node_settings->ParentWindowId)
19681 buf->appendf(" Window=0x%08X", node_settings->ParentWindowId);
19682 buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y);
19683 }
19684 if (node_settings->SplitAxis != ImGuiAxis_None)
19685 buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y');
19686 if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
19687 buf->appendf(" NoResize=1");
19688 if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode)
19689 buf->appendf(" CentralNode=1");
19690 if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar)
19691 buf->appendf(" NoTabBar=1");
19692 if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar)
19693 buf->appendf(" HiddenTabBar=1");
19694 if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton)
19695 buf->appendf(" NoWindowMenuButton=1");
19696 if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton)
19697 buf->appendf(" NoCloseButton=1");
19698 if (node_settings->SelectedTabId)
19699 buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId);
19700
19701 // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!)
19702 if (g.IO.ConfigDebugIniSettings)
19703 if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID))
19704 {
19705 buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything
19706 if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow)
19707 buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name);
19708 // Iterate settings so we can give info about windows that didn't exist during the session.
19709 int contains_window = 0;
19710 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
19711 if (settings->DockId == node_settings->ID)
19712 {
19713 if (contains_window++ == 0)
19714 buf->appendf(" ; contains ");
19715 buf->appendf("'%s' ", settings->GetName());
19716 }
19717 }
19718
19719 buf->appendf("\n");
19720 }
19721 buf->appendf("\n");
19722}
19723
19724
19725//-----------------------------------------------------------------------------
19726// [SECTION] PLATFORM DEPENDENT HELPERS
19727//-----------------------------------------------------------------------------
19728
19729#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
19730
19731#ifdef _MSC_VER
19732#pragma comment(lib, "user32")
19733#pragma comment(lib, "kernel32")
19734#endif
19735
19736// Win32 clipboard implementation
19737// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
19738static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)
19739{
19740 ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
19741 g.ClipboardHandlerData.clear();
19742 if (!::OpenClipboard(NULL))
19743 return NULL;
19744 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
19745 if (wbuf_handle == NULL)
19746 {
19747 ::CloseClipboard();
19748 return NULL;
19749 }
19750 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
19751 {
19752 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
19753 g.ClipboardHandlerData.resize(buf_len);
19754 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
19755 }
19756 ::GlobalUnlock(wbuf_handle);
19757 ::CloseClipboard();
19758 return g.ClipboardHandlerData.Data;
19759}
19760
19761static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
19762{
19763 if (!::OpenClipboard(NULL))
19764 return;
19765 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
19766 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
19767 if (wbuf_handle == NULL)
19768 {
19769 ::CloseClipboard();
19770 return;
19771 }
19772 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
19773 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
19774 ::GlobalUnlock(wbuf_handle);
19775 ::EmptyClipboard();
19776 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
19777 ::GlobalFree(wbuf_handle);
19778 ::CloseClipboard();
19779}
19780
19781#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
19782
19783#include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
19784static PasteboardRef main_clipboard = 0;
19785
19786// OSX clipboard implementation
19787// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
19788static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
19789{
19790 if (!main_clipboard)
19791 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
19792 PasteboardClear(main_clipboard);
19793 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
19794 if (cf_data)
19795 {
19796 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
19797 CFRelease(cf_data);
19798 }
19799}
19800
19801static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)
19802{
19803 ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
19804 if (!main_clipboard)
19805 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
19806 PasteboardSynchronize(main_clipboard);
19807
19808 ItemCount item_count = 0;
19809 PasteboardGetItemCount(main_clipboard, &item_count);
19810 for (ItemCount i = 0; i < item_count; i++)
19811 {
19812 PasteboardItemID item_id = 0;
19813 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
19814 CFArrayRef flavor_type_array = 0;
19815 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
19816 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
19817 {
19818 CFDataRef cf_data;
19819 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
19820 {
19821 g.ClipboardHandlerData.clear();
19822 int length = (int)CFDataGetLength(cf_data);
19823 g.ClipboardHandlerData.resize(length + 1);
19824 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
19825 g.ClipboardHandlerData[length] = 0;
19826 CFRelease(cf_data);
19827 return g.ClipboardHandlerData.Data;
19828 }
19829 }
19830 }
19831 return NULL;
19832}
19833
19834#else
19835
19836// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
19837static const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)
19838{
19839 ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
19840 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
19841}
19842
19843static void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text)
19844{
19845 ImGuiContext& g = *(ImGuiContext*)user_data_ctx;
19846 g.ClipboardHandlerData.clear();
19847 const char* text_end = text + strlen(text);
19848 g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
19849 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
19850 g.ClipboardHandlerData[(int)(text_end - text)] = 0;
19851}
19852
19853#endif
19854
19855// Win32 API IME support (for Asian languages, etc.)
19856#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
19857
19858#include <imm.h>
19859#ifdef _MSC_VER
19860#pragma comment(lib, "imm32")
19861#endif
19862
19863static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data)
19864{
19865 // Notify OS Input Method Editor of text input position
19866 HWND hwnd = (HWND)viewport->PlatformHandleRaw;
19867 if (hwnd == 0)
19868 return;
19869
19870 //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
19871 if (HIMC himc = ::ImmGetContext(hwnd))
19872 {
19873 COMPOSITIONFORM composition_form = {};
19874 composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
19875 composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
19876 composition_form.dwStyle = CFS_FORCE_POSITION;
19877 ::ImmSetCompositionWindow(himc, &composition_form);
19878 CANDIDATEFORM candidate_form = {};
19879 candidate_form.dwStyle = CFS_CANDIDATEPOS;
19880 candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x);
19881 candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y);
19882 ::ImmSetCandidateWindow(himc, &candidate_form);
19883 ::ImmReleaseContext(hwnd, himc);
19884 }
19885}
19886
19887#else
19888
19889static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {}
19890
19891#endif
19892
19893//-----------------------------------------------------------------------------
19894// [SECTION] METRICS/DEBUGGER WINDOW
19895//-----------------------------------------------------------------------------
19896// - DebugRenderViewportThumbnail() [Internal]
19897// - RenderViewportsThumbnails() [Internal]
19898// - DebugTextEncoding()
19899// - MetricsHelpMarker() [Internal]
19900// - ShowFontAtlas() [Internal]
19901// - ShowMetricsWindow()
19902// - DebugNodeColumns() [Internal]
19903// - DebugNodeDockNode() [Internal]
19904// - DebugNodeDrawList() [Internal]
19905// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
19906// - DebugNodeFont() [Internal]
19907// - DebugNodeFontGlyph() [Internal]
19908// - DebugNodeStorage() [Internal]
19909// - DebugNodeTabBar() [Internal]
19910// - DebugNodeViewport() [Internal]
19911// - DebugNodeWindow() [Internal]
19912// - DebugNodeWindowSettings() [Internal]
19913// - DebugNodeWindowsList() [Internal]
19914// - DebugNodeWindowsListByBeginStackParent() [Internal]
19915//-----------------------------------------------------------------------------
19916
19917#ifndef IMGUI_DISABLE_DEBUG_TOOLS
19918
19919void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
19920{
19921 ImGuiContext& g = *GImGui;
19922 ImGuiWindow* window = g.CurrentWindow;
19923
19924 ImVec2 scale = bb.GetSize() / viewport->Size;
19925 ImVec2 off = bb.Min - viewport->Pos * scale;
19926 float alpha_mul = (viewport->Flags & ImGuiViewportFlags_IsMinimized) ? 0.30f : 1.00f;
19927 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
19928 for (ImGuiWindow* thumb_window : g.Windows)
19929 {
19930 if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
19931 continue;
19932 if (thumb_window->Viewport != viewport)
19933 continue;
19934
19935 ImRect thumb_r = thumb_window->Rect();
19936 ImRect title_r = thumb_window->TitleBarRect();
19937 thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale));
19938 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
19939 thumb_r.ClipWithFull(bb);
19940 title_r.ClipWithFull(bb);
19941 const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
19942 window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
19943 window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
19944 window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
19945 window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
19946 }
19947 draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
19948 if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
19949 window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
19950}
19951
19952static void RenderViewportsThumbnails()
19953{
19954 ImGuiContext& g = *GImGui;
19955 ImGuiWindow* window = g.CurrentWindow;
19956
19957 // Draw monitor and calculate their boundaries
19958 float SCALE = 1.0f / 8.0f;
19959 ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
19960 for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
19961 bb_full.Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize));
19962 ImVec2 p = window->DC.CursorPos;
19963 ImVec2 off = p - bb_full.Min * SCALE;
19964 for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors)
19965 {
19966 ImRect monitor_draw_bb(off + (monitor.MainPos) * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE);
19967 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);
19968 window->DrawList->AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.10f), 4.0f);
19969 }
19970
19971 // Draw viewports
19972 for (ImGuiViewportP* viewport : g.Viewports)
19973 {
19974 ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
19975 ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
19976 }
19977 ImGui::Dummy(bb_full.GetSize() * SCALE);
19978}
19979
19980static int IMGUI_CDECL ViewportComparerByLastFocusedStampCount(const void* lhs, const void* rhs)
19981{
19982 const ImGuiViewportP* a = *(const ImGuiViewportP* const*)lhs;
19983 const ImGuiViewportP* b = *(const ImGuiViewportP* const*)rhs;
19984 return b->LastFocusedStampCount - a->LastFocusedStampCount;
19985}
19986
19987// Draw an arbitrary US keyboard layout to visualize translated keys
19988void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
19989{
19990 const float scale = ImGui::GetFontSize() / 13.0f;
19991 const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
19992 const float key_rounding = 3.0f * scale;
19993 const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
19994 const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
19995 const float key_face_rounding = 2.0f * scale;
19996 const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
19997 const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
19998 const float key_row_offset = 9.0f * scale;
19999
20000 ImVec2 board_min = GetCursorScreenPos();
20001 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);
20002 ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
20003
20004 struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
20005 const KeyLayoutData keys_to_display[] =
20006 {
20007 { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
20008 { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
20009 { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
20010 };
20011
20012 // Elements rendered manually via ImDrawList API are not clipped automatically.
20013 // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
20014 Dummy(board_max - board_min);
20015 if (!IsItemVisible())
20016 return;
20017 draw_list->PushClipRect(board_min, board_max, true);
20018 for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
20019 {
20020 const KeyLayoutData* key_data = &keys_to_display[n];
20021 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);
20022 ImVec2 key_max = key_min + key_size;
20023 draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
20024 draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
20025 ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
20026 ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
20027 draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
20028 draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
20029 ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
20030 draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
20031 if (IsKeyDown(key_data->Key))
20032 draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
20033 }
20034 draw_list->PopClipRect();
20035}
20036
20037// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
20038void ImGui::DebugTextEncoding(const char* str)
20039{
20040 Text("Text: \"%s\"", str);
20041 if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
20042 return;
20043 TableSetupColumn("Offset");
20044 TableSetupColumn("UTF-8");
20045 TableSetupColumn("Glyph");
20046 TableSetupColumn("Codepoint");
20047 TableHeadersRow();
20048 for (const char* p = str; *p != 0; )
20049 {
20050 unsigned int c;
20051 const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
20052 TableNextColumn();
20053 Text("%d", (int)(p - str));
20054 TableNextColumn();
20055 for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
20056 {
20057 if (byte_index > 0)
20058 SameLine();
20059 Text("0x%02X", (int)(unsigned char)p[byte_index]);
20060 }
20061 TableNextColumn();
20062 if (GetFont()->FindGlyphNoFallback((ImWchar)c))
20063 TextUnformatted(p, p + c_utf8_len);
20064 else
20065 TextUnformatted((c == IM_UNICODE_CODEPOINT_INVALID) ? "[invalid]" : "[missing]");
20066 TableNextColumn();
20067 Text("U+%04X", (int)c);
20068 p += c_utf8_len;
20069 }
20070 EndTable();
20071}
20072
20073static void DebugFlashStyleColorStop()
20074{
20075 ImGuiContext& g = *GImGui;
20076 if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
20077 g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
20078 g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
20079}
20080
20081// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
20082void ImGui::DebugFlashStyleColor(ImGuiCol idx)
20083{
20084 ImGuiContext& g = *GImGui;
20085 DebugFlashStyleColorStop();
20086 g.DebugFlashStyleColorTime = 0.5f;
20087 g.DebugFlashStyleColorIdx = idx;
20088 g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
20089}
20090
20091void ImGui::UpdateDebugToolFlashStyleColor()
20092{
20093 ImGuiContext& g = *GImGui;
20094 if (g.DebugFlashStyleColorTime <= 0.0f)
20095 return;
20096 ColorConvertHSVtoRGB(cosf(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);
20097 g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
20098 if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
20099 DebugFlashStyleColorStop();
20100}
20101
20102// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
20103static void MetricsHelpMarker(const char* desc)
20104{
20105 ImGui::TextDisabled("(?)");
20106 if (ImGui::BeginItemTooltip())
20107 {
20108 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
20110 ImGui::PopTextWrapPos();
20111 ImGui::EndTooltip();
20112 }
20113}
20114
20115// [DEBUG] List fonts in a font atlas and display its texture
20116void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
20117{
20118 for (ImFont* font : atlas->Fonts)
20119 {
20120 PushID(font);
20121 DebugNodeFont(font);
20122 PopID();
20123 }
20124 if (TreeNode("Font Atlas", "Font Atlas (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
20125 {
20126 ImGuiContext& g = *GImGui;
20127 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
20128 Checkbox("Tint with Text Color", &cfg->ShowAtlasTintedWithTextColor); // Using text color ensure visibility of core atlas data, but will alter custom colored icons
20129 ImVec4 tint_col = cfg->ShowAtlasTintedWithTextColor ? GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
20130 ImVec4 border_col = GetStyleColorVec4(ImGuiCol_Border);
20131 Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
20132 TreePop();
20133 }
20134}
20135
20136void ImGui::ShowMetricsWindow(bool* p_open)
20137{
20138 ImGuiContext& g = *GImGui;
20139 ImGuiIO& io = g.IO;
20140 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
20141 if (cfg->ShowDebugLog)
20142 ShowDebugLogWindow(&cfg->ShowDebugLog);
20143 if (cfg->ShowIDStackTool)
20144 ShowIDStackToolWindow(&cfg->ShowIDStackTool);
20145
20146 if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
20147 {
20148 End();
20149 return;
20150 }
20151
20152 // [DEBUG] Clear debug breaks hooks after exactly one cycle.
20153 DebugBreakClearData();
20154
20155 // Basic info
20156 Text("Dear ImGui %s", GetVersion());
20157 Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
20158 Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
20159 Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
20160 //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
20161
20162 Separator();
20163
20164 // Debugging enums
20165 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
20166 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
20167 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
20168 const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
20169 if (cfg->ShowWindowsRectsType < 0)
20170 cfg->ShowWindowsRectsType = WRT_WorkRect;
20171 if (cfg->ShowTablesRectsType < 0)
20172 cfg->ShowTablesRectsType = TRT_WorkRect;
20173
20174 struct Funcs
20175 {
20176 static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
20177 {
20178 ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
20179 if (rect_type == TRT_OuterRect) { return table->OuterRect; }
20180 else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
20181 else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
20182 else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
20183 else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
20184 else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
20185 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); }
20186 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); }
20187 else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
20188 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
20189 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); }
20190 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); }
20191 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); }
20192 IM_ASSERT(0);
20193 return ImRect();
20194 }
20195
20196 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
20197 {
20198 if (rect_type == WRT_OuterRect) { return window->Rect(); }
20199 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
20200 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
20201 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
20202 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
20203 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
20204 else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
20205 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
20206 IM_ASSERT(0);
20207 return ImRect();
20208 }
20209 };
20210
20211 // Tools
20212 if (TreeNode("Tools"))
20213 {
20214 // Debug Break features
20215 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
20216 SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
20217 SameLine();
20218 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.");
20219 if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
20220 DebugStartItemPicker();
20221 Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
20222
20223 SeparatorText("Visualize");
20224
20225 Checkbox("Show Debug Log", &cfg->ShowDebugLog);
20226 SameLine();
20227 MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
20228
20229 Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
20230 SameLine();
20231 MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
20232
20233 Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
20234 Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
20235 SameLine();
20236 SetNextItemWidth(GetFontSize() * 12);
20237 cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
20238 if (cfg->ShowWindowsRects && g.NavWindow != NULL)
20239 {
20240 BulletText("'%s':", g.NavWindow->Name);
20241 Indent();
20242 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
20243 {
20244 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
20245 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]);
20246 }
20247 Unindent();
20248 }
20249
20250 Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
20251 SameLine();
20252 SetNextItemWidth(GetFontSize() * 12);
20253 cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
20254 if (cfg->ShowTablesRects && g.NavWindow != NULL)
20255 {
20256 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
20257 {
20258 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
20259 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
20260 continue;
20261
20262 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
20263 if (IsItemHovered())
20264 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);
20265 Indent();
20266 char buf[128];
20267 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
20268 {
20269 if (rect_n >= TRT_ColumnsRect)
20270 {
20271 if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
20272 continue;
20273 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
20274 {
20275 ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
20276 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]);
20277 Selectable(buf);
20278 if (IsItemHovered())
20279 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
20280 }
20281 }
20282 else
20283 {
20284 ImRect r = Funcs::GetTableRect(table, rect_n, -1);
20285 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]);
20286 Selectable(buf);
20287 if (IsItemHovered())
20288 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
20289 }
20290 }
20291 Unindent();
20292 }
20293 }
20294 Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
20295
20296 SeparatorText("Validate");
20297
20298 Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
20299 SameLine();
20300 MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
20301
20302 Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
20303 SameLine();
20304 MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
20305 if (cfg->ShowTextEncodingViewer)
20306 {
20307 static char buf[64] = "";
20308 SetNextItemWidth(-FLT_MIN);
20309 InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
20310 if (buf[0] != 0)
20311 DebugTextEncoding(buf);
20312 }
20313
20314 TreePop();
20315 }
20316
20317 // Windows
20318 if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
20319 {
20320 //SetNextItemOpen(true, ImGuiCond_Once);
20321 DebugNodeWindowsList(&g.Windows, "By display order");
20322 DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
20323 if (TreeNode("By submission order (begin stack)"))
20324 {
20325 // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
20326 ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
20327 temp_buffer.resize(0);
20328 for (ImGuiWindow* window : g.Windows)
20329 if (window->LastFrameActive + 1 >= g.FrameCount)
20330 temp_buffer.push_back(window);
20331 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); } };
20332 ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
20333 DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
20334 TreePop();
20335 }
20336
20337 TreePop();
20338 }
20339
20340 // DrawLists
20341 int drawlist_count = 0;
20342 for (ImGuiViewportP* viewport : g.Viewports)
20343 drawlist_count += viewport->DrawDataP.CmdLists.Size;
20344 if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
20345 {
20346 Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
20347 Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
20348 for (ImGuiViewportP* viewport : g.Viewports)
20349 {
20350 bool viewport_has_drawlist = false;
20351 for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
20352 {
20353 if (!viewport_has_drawlist)
20354 Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID);
20355 viewport_has_drawlist = true;
20356 DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
20357 }
20358 }
20359 TreePop();
20360 }
20361
20362 // Viewports
20363 if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
20364 {
20365 cfg->HighlightMonitorIdx = -1;
20366 bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size);
20367 SameLine();
20368 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.");
20369 if (open)
20370 {
20371 for (int i = 0; i < g.PlatformIO.Monitors.Size; i++)
20372 {
20373 const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i];
20374 BulletText("Monitor #%d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)",
20375 i, mon.DpiScale * 100.0f,
20376 mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y,
20377 mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y);
20378 if (IsItemHovered())
20379 cfg->HighlightMonitorIdx = i;
20380 }
20381 TreePop();
20382 }
20383
20384 SetNextItemOpen(true, ImGuiCond_Once);
20385 if (TreeNode("Windows Minimap"))
20386 {
20387 RenderViewportsThumbnails();
20388 TreePop();
20389 }
20390 cfg->HighlightViewportID = 0;
20391
20392 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);
20393 if (TreeNode("Inferred Z order (front-to-back)"))
20394 {
20395 static ImVector<ImGuiViewportP*> viewports;
20396 viewports.resize(g.Viewports.Size);
20397 memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes());
20398 if (viewports.Size > 1)
20399 ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByLastFocusedStampCount);
20400 for (ImGuiViewportP* viewport : viewports)
20401 {
20402 BulletText("Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \"%s\"",
20403 viewport->Idx, viewport->ID, viewport->LastFocusedStampCount,
20404 (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? "1" : "0") : "N/A",
20405 viewport->Window ? viewport->Window->Name : "N/A");
20406 if (IsItemHovered())
20407 cfg->HighlightViewportID = viewport->ID;
20408 }
20409 TreePop();
20410 }
20411
20412 for (ImGuiViewportP* viewport : g.Viewports)
20413 DebugNodeViewport(viewport);
20414 TreePop();
20415 }
20416
20417 // Details for Popups
20418 if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
20419 {
20420 for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
20421 {
20422 // As it's difficult to interact with tree nodes while popups are open, we display everything inline.
20423 ImGuiWindow* window = popup_data.Window;
20424 BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
20425 popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
20426 popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
20427 }
20428 TreePop();
20429 }
20430
20431 // Details for TabBars
20432 if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
20433 {
20434 for (int n = 0; n < g.TabBars.GetMapSize(); n++)
20435 if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
20436 {
20437 PushID(tab_bar);
20438 DebugNodeTabBar(tab_bar, "TabBar");
20439 PopID();
20440 }
20441 TreePop();
20442 }
20443
20444 // Details for Tables
20445 if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
20446 {
20447 for (int n = 0; n < g.Tables.GetMapSize(); n++)
20448 if (ImGuiTable* table = g.Tables.TryGetMapData(n))
20449 DebugNodeTable(table);
20450 TreePop();
20451 }
20452
20453 // Details for Fonts
20454 ImFontAtlas* atlas = g.IO.Fonts;
20455 if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
20456 {
20457 ShowFontAtlas(atlas);
20458 TreePop();
20459 }
20460
20461 // Details for InputText
20462 if (TreeNode("InputText"))
20463 {
20464 DebugNodeInputTextState(&g.InputTextState);
20465 TreePop();
20466 }
20467
20468 // Details for TypingSelect
20469 if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
20470 {
20471 DebugNodeTypingSelectState(&g.TypingSelectState);
20472 TreePop();
20473 }
20474
20475 // Details for Docking
20476#ifdef IMGUI_HAS_DOCK
20477 if (TreeNode("Docking"))
20478 {
20479 static bool root_nodes_only = true;
20480 ImGuiDockContext* dc = &g.DockContext;
20481 Checkbox("List root nodes", &root_nodes_only);
20482 Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes);
20483 if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); }
20484 SameLine();
20485 if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; }
20486 for (int n = 0; n < dc->Nodes.Data.Size; n++)
20487 if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
20488 if (!root_nodes_only || node->IsRootNode())
20489 DebugNodeDockNode(node, "Node");
20490 TreePop();
20491 }
20492#endif // #ifdef IMGUI_HAS_DOCK
20493
20494 // Settings
20495 if (TreeNode("Settings"))
20496 {
20497 if (SmallButton("Clear"))
20498 ClearIniSettings();
20499 SameLine();
20500 if (SmallButton("Save to memory"))
20501 SaveIniSettingsToMemory();
20502 SameLine();
20503 if (SmallButton("Save to disk"))
20504 SaveIniSettingsToDisk(g.IO.IniFilename);
20505 SameLine();
20506 if (g.IO.IniFilename)
20507 Text("\"%s\"", g.IO.IniFilename);
20508 else
20509 TextUnformatted("<NULL>");
20510 Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
20511 Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
20512 if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
20513 {
20514 for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
20515 BulletText("\"%s\"", handler.TypeName);
20516 TreePop();
20517 }
20518 if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
20519 {
20520 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
20521 DebugNodeWindowSettings(settings);
20522 TreePop();
20523 }
20524
20525 if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
20526 {
20527 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
20528 DebugNodeTableSettings(settings);
20529 TreePop();
20530 }
20531
20532#ifdef IMGUI_HAS_DOCK
20533 if (TreeNode("SettingsDocking", "Settings packed data: Docking"))
20534 {
20535 ImGuiDockContext* dc = &g.DockContext;
20536 Text("In SettingsWindows:");
20537 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
20538 if (settings->DockId != 0)
20539 BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings->GetName(), settings->DockId, settings->DockOrder);
20540 Text("In SettingsNodes:");
20541 for (int n = 0; n < dc->NodesSettings.Size; n++)
20542 {
20543 ImGuiDockNodeSettings* settings = &dc->NodesSettings[n];
20544 const char* selected_tab_name = NULL;
20545 if (settings->SelectedTabId)
20546 {
20547 if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId))
20548 selected_tab_name = window->Name;
20549 else if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->SelectedTabId))
20550 selected_tab_name = window_settings->GetName();
20551 }
20552 BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : "");
20553 }
20554 TreePop();
20555 }
20556#endif // #ifdef IMGUI_HAS_DOCK
20557
20558 if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
20559 {
20560 InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
20561 TreePop();
20562 }
20563 TreePop();
20564 }
20565
20566 // Settings
20567 if (TreeNode("Memory allocations"))
20568 {
20569 ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
20570 Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
20571 if (SmallButton("GC now")) { g.GcCompactAll = true; }
20572 Text("Recent frames with allocations:");
20573 int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
20574 for (int n = buf_size - 1; n >= 0; n--)
20575 {
20576 ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
20577 BulletText("Frame %06d: %+3d ( %2d malloc, %2d free )%s", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount, (n == 0) ? " (most recent)" : "");
20578 }
20579 TreePop();
20580 }
20581
20582 if (TreeNode("Inputs"))
20583 {
20584 Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
20585 {
20586 // 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.
20587 // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
20588 Indent();
20589#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
20590 struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
20591#else
20592 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
20593 //Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } }
20594#endif
20595 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); }
20596 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); }
20597 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); }
20598 Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
20599 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.
20600 DebugRenderKeyboardPreview(GetWindowDrawList());
20601 Unindent();
20602 }
20603
20604 Text("MOUSE STATE");
20605 {
20606 Indent();
20607 if (IsMousePosValid())
20608 Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
20609 else
20610 Text("Mouse pos: <INVALID>");
20611 Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
20612 int count = IM_ARRAYSIZE(io.MouseDown);
20613 Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
20614 Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
20615 Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
20616 Text("Mouse wheel: %.1f", io.MouseWheel);
20617 Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
20618 Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
20619 Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
20620 Unindent();
20621 }
20622
20623 Text("MOUSE WHEELING");
20624 {
20625 Indent();
20626 Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
20627 Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
20628 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>");
20629 Unindent();
20630 }
20631
20632 Text("KEY OWNERS");
20633 {
20634 Indent();
20635 if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
20636 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
20637 {
20638 ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
20639 if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
20640 continue;
20641 Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
20642 owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
20643 DebugLocateItemOnHover(owner_data->OwnerCurr);
20644 }
20645 EndChild();
20646 Unindent();
20647 }
20648 Text("SHORTCUT ROUTING");
20649 SameLine();
20650 MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
20651 {
20652 Indent();
20653 if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
20654 for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
20655 {
20656 ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
20657 for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
20658 {
20659 ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
20660 ImGuiKeyChord key_chord = key | routing_data->Mods;
20661 Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
20662 DebugLocateItemOnHover(routing_data->RoutingCurr);
20663 if (g.IO.ConfigDebugIsDebuggerPresent)
20664 {
20665 SameLine();
20666 if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
20667 g.DebugBreakInShortcutRouting = key_chord;
20668 }
20669 idx = routing_data->NextEntryIndex;
20670 }
20671 }
20672 EndChild();
20673 Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
20674 Unindent();
20675 }
20676 TreePop();
20677 }
20678
20679 if (TreeNode("Internal state"))
20680 {
20681 Text("WINDOWING");
20682 Indent();
20683 Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
20684 Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL");
20685 Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
20686 Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0);
20687 Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
20688 Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
20689 Unindent();
20690
20691 Text("ITEMS");
20692 Indent();
20693 Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
20694 DebugLocateItemOnHover(g.ActiveId);
20695 Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
20696 Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
20697 Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
20698 Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
20699 Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
20700 DebugLocateItemOnHover(g.DragDropPayload.SourceId);
20701 Unindent();
20702
20703 Text("NAV,FOCUS");
20704 Indent();
20705 Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
20706 Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
20707 DebugLocateItemOnHover(g.NavId);
20708 Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
20709 Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
20710 Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
20711 Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
20712 Text("NavActivateFlags: %04X", g.NavActivateFlags);
20713 Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
20714 Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
20715 Text("NavFocusRoute[] = ");
20716 for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
20717 {
20718 const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
20719 SameLine(0.0f, 0.0f);
20720 Text("0x%08X/", focus_scope.ID);
20721 SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
20722 }
20723 Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
20724 Unindent();
20725
20726 TreePop();
20727 }
20728
20729 // Overlay: Display windows Rectangles and Begin Order
20730 if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
20731 {
20732 for (ImGuiWindow* window : g.Windows)
20733 {
20734 if (!window->WasActive)
20735 continue;
20736 ImDrawList* draw_list = GetForegroundDrawList(window);
20737 if (cfg->ShowWindowsRects)
20738 {
20739 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
20740 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
20741 }
20742 if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
20743 {
20744 char buf[32];
20745 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
20746 float font_size = GetFontSize();
20747 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
20748 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
20749 }
20750 }
20751 }
20752
20753 // Overlay: Display Tables Rectangles
20754 if (cfg->ShowTablesRects)
20755 {
20756 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
20757 {
20758 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
20759 if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
20760 continue;
20761 ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
20762 if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
20763 {
20764 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
20765 {
20766 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
20767 ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
20768 float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
20769 draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
20770 }
20771 }
20772 else
20773 {
20774 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
20775 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
20776 }
20777 }
20778 }
20779
20780#ifdef IMGUI_HAS_DOCK
20781 // Overlay: Display Docking info
20782 if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode)
20783 {
20784 char buf[64] = "";
20785 char* p = buf;
20786 ImGuiDockNode* node = g.DebugHoveredDockNode;
20787 ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport());
20788 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : "");
20789 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId);
20790 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
20791 p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y);
20792 int depth = DockNodeGetDepth(node);
20793 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));
20794 ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth;
20795 overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
20796 overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
20797 }
20798#endif // #ifdef IMGUI_HAS_DOCK
20799
20800 End();
20801}
20802
20803void ImGui::DebugBreakClearData()
20804{
20805 // Those fields are scattered in their respective subsystem to stay in hot-data locations
20806 ImGuiContext& g = *GImGui;
20807 g.DebugBreakInWindow = 0;
20808 g.DebugBreakInTable = 0;
20809 g.DebugBreakInShortcutRouting = ImGuiKey_None;
20810}
20811
20812void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
20813{
20814 if (!BeginItemTooltip())
20815 return;
20816 Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
20817 Separator();
20818 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.");
20819 Separator();
20820 TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
20821 EndTooltip();
20822}
20823
20824// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
20825// In order to reduce interferences with the contents we are trying to debug into.
20826bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
20827{
20828 ImGuiWindow* window = GetCurrentWindow();
20829 if (window->SkipItems)
20830 return false;
20831
20832 ImGuiContext& g = *GImGui;
20833 const ImGuiID id = window->GetID(label);
20834 const ImVec2 label_size = CalcTextSize(label, NULL, true);
20835 ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
20836 ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
20837
20838 const ImRect bb(pos, pos + size);
20839 ItemSize(size, 0.0f);
20840 if (!ItemAdd(bb, id))
20841 return false;
20842
20843 // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
20844 bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
20845 bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
20846 DebugBreakButtonTooltip(false, description_of_location);
20847
20848 ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
20849 ImVec4 hsv;
20850 ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
20851 ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
20852
20853 RenderNavHighlight(bb, id);
20854 RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
20855 RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
20856
20857 IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
20858 return pressed;
20859}
20860
20861// [DEBUG] Display contents of Columns
20862void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
20863{
20864 if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
20865 return;
20866 BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
20867 for (ImGuiOldColumnData& column : columns->Columns)
20868 BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
20869 TreePop();
20870}
20871
20872static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled)
20873{
20874 using namespace ImGui;
20875 PushID(label);
20876 PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
20877 Text("%s:", label);
20878 if (!enabled)
20879 BeginDisabled();
20880 CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize);
20881 CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX);
20882 CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY);
20883 CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar);
20884 CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar);
20885 CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton);
20886 CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton);
20887 CheckboxFlags("DockedWindowsInFocusRoute", p_flags, ImGuiDockNodeFlags_DockedWindowsInFocusRoute);
20888 CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking); // Multiple flags
20889 CheckboxFlags("NoDockingSplit", p_flags, ImGuiDockNodeFlags_NoDockingSplit);
20890 CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther);
20891 CheckboxFlags("NoDockingOver", p_flags, ImGuiDockNodeFlags_NoDockingOverMe);
20892 CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther);
20893 CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty);
20894 CheckboxFlags("NoUndocking", p_flags, ImGuiDockNodeFlags_NoUndocking);
20895 if (!enabled)
20896 EndDisabled();
20897 PopStyleVar();
20898 PopID();
20899}
20900
20901// [DEBUG] Display contents of ImDockNode
20902void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label)
20903{
20904 ImGuiContext& g = *GImGui;
20905 const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2); // Submitted with ImGuiDockNodeFlags_KeepAliveOnly
20906 const bool is_active = (g.FrameCount - node->LastFrameActive < 2); // Submitted
20907 if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
20908 bool open;
20909 ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
20910 if (node->Windows.Size > 0)
20911 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");
20912 else
20913 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");
20914 if (!is_alive) { PopStyleColor(); }
20915 if (is_active && IsItemHovered())
20916 if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow)
20917 GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255));
20918 if (open)
20919 {
20920 IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
20921 IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
20922 BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)",
20923 node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y);
20924 DebugNodeWindow(node->HostWindow, "HostWindow");
20925 DebugNodeWindow(node->VisibleWindow, "VisibleWindow");
20926 BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId);
20927 BulletText("Misc:%s%s%s%s%s%s%s",
20928 node->IsDockSpace() ? " IsDockSpace" : "",
20929 node->IsCentralNode() ? " IsCentralNode" : "",
20930 is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->IsFocused ? " IsFocused" : "",
20931 node->WantLockSizeOnce ? " WantLockSizeOnce" : "",
20932 node->HasCentralNodeChild ? " HasCentralNodeChild" : "");
20933 if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags))
20934 {
20935 if (BeginTable("flags", 4))
20936 {
20937 TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false);
20938 TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true);
20939 TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false);
20940 TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true);
20941 EndTable();
20942 }
20943 TreePop();
20944 }
20945 if (node->ParentNode)
20946 DebugNodeDockNode(node->ParentNode, "ParentNode");
20947 if (node->ChildNodes[0])
20948 DebugNodeDockNode(node->ChildNodes[0], "Child[0]");
20949 if (node->ChildNodes[1])
20950 DebugNodeDockNode(node->ChildNodes[1], "Child[1]");
20951 if (node->TabBar)
20952 DebugNodeTabBar(node->TabBar, "TabBar");
20953 DebugNodeWindowsList(&node->Windows, "Windows");
20954
20955 TreePop();
20956 }
20957}
20958
20959static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
20960{
20961 union { void* ptr; int integer; } tex_id_opaque;
20962 memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
20963 if (sizeof(tex_id) >= sizeof(void*))
20964 ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
20965 else
20966 ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
20967}
20968
20969// [DEBUG] Display contents of ImDrawList
20970// Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport.
20971void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
20972{
20973 ImGuiContext& g = *GImGui;
20974 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
20975 int cmd_count = draw_list->CmdBuffer.Size;
20976 if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
20977 cmd_count--;
20978 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);
20979 if (draw_list == GetWindowDrawList())
20980 {
20981 SameLine();
20982 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)
20983 if (node_open)
20984 TreePop();
20985 return;
20986 }
20987
20988 ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list
20989 if (window && IsItemHovered() && fg_draw_list)
20990 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
20991 if (!node_open)
20992 return;
20993
20994 if (window && !window->WasActive)
20995 TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
20996
20997 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
20998 {
20999 if (pcmd->UserCallback)
21000 {
21001 BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
21002 continue;
21003 }
21004
21005 char texid_desc[20];
21006 FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId);
21007 char buf[300];
21008 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
21009 pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
21010 bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
21011 if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
21012 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
21013 if (!pcmd_node_open)
21014 continue;
21015
21016 // Calculate approximate coverage area (touched pixel count)
21017 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
21018 const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
21019 const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
21020 float total_area = 0.0f;
21021 for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
21022 {
21023 ImVec2 triangle[3];
21024 for (int n = 0; n < 3; n++, idx_n++)
21025 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
21026 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
21027 }
21028
21029 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
21030 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
21031 Selectable(buf);
21032 if (IsItemHovered() && fg_draw_list)
21033 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
21034
21035 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
21036 ImGuiListClipper clipper;
21037 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
21038 while (clipper.Step())
21039 for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
21040 {
21041 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
21042 ImVec2 triangle[3];
21043 for (int n = 0; n < 3; n++, idx_i++)
21044 {
21045 const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
21046 triangle[n] = v.pos;
21047 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
21048 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
21049 }
21050
21051 Selectable(buf, false);
21052 if (fg_draw_list && IsItemHovered())
21053 {
21054 ImDrawListFlags backup_flags = fg_draw_list->Flags;
21055 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
21056 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
21057 fg_draw_list->Flags = backup_flags;
21058 }
21059 }
21060 TreePop();
21061 }
21062 TreePop();
21063}
21064
21065// [DEBUG] Display mesh/aabb of a ImDrawCmd
21066void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
21067{
21068 IM_ASSERT(show_mesh || show_aabb);
21069
21070 // Draw wire-frame version of all triangles
21071 ImRect clip_rect = draw_cmd->ClipRect;
21072 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
21073 ImDrawListFlags backup_flags = out_draw_list->Flags;
21074 out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
21075 for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
21076 {
21077 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
21078 ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
21079
21080 ImVec2 triangle[3];
21081 for (int n = 0; n < 3; n++, idx_n++)
21082 vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
21083 if (show_mesh)
21084 out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
21085 }
21086 // Draw bounding boxes
21087 if (show_aabb)
21088 {
21089 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
21090 out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
21091 }
21092 out_draw_list->Flags = backup_flags;
21093}
21094
21095// [DEBUG] Display details for a single font, called by ShowStyleEditor().
21096void ImGui::DebugNodeFont(ImFont* font)
21097{
21098 bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
21099 font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
21100 SameLine();
21101 if (SmallButton("Set as default"))
21102 GetIO().FontDefault = font;
21103 if (!opened)
21104 return;
21105
21106 // Display preview text
21107 PushFont(font);
21108 Text("The quick brown fox jumps over the lazy dog");
21109 PopFont();
21110
21111 // Display details
21112 SetNextItemWidth(GetFontSize() * 8);
21113 DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
21114 SameLine(); MetricsHelpMarker(
21115 "Note that the default embedded font is NOT meant to be scaled.\n\n"
21116 "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
21117 "You may oversample them to get some flexibility with scaling. "
21118 "You can also render at multiple sizes and select which one to use at runtime.\n\n"
21119 "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
21120 Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
21121 char c_str[5];
21122 Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
21123 Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
21124 const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
21125 Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
21126 for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
21127 if (font->ConfigData)
21128 if (const ImFontConfig* cfg = &font->ConfigData[config_i])
21129 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
21130 config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
21131
21132 // Display all glyphs of the fonts in separate pages of 256 characters
21133 if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
21134 {
21135 ImDrawList* draw_list = GetWindowDrawList();
21136 const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
21137 const float cell_size = font->FontSize * 1;
21138 const float cell_spacing = GetStyle().ItemSpacing.y;
21139 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
21140 {
21141 // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
21142 // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
21143 // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
21144 if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
21145 {
21146 base += 4096 - 256;
21147 continue;
21148 }
21149
21150 int count = 0;
21151 for (unsigned int n = 0; n < 256; n++)
21152 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
21153 count++;
21154 if (count <= 0)
21155 continue;
21156 if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
21157 continue;
21158
21159 // Draw a 16x16 grid of glyphs
21160 ImVec2 base_pos = GetCursorScreenPos();
21161 for (unsigned int n = 0; n < 256; n++)
21162 {
21163 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
21164 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
21165 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
21166 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
21167 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
21168 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
21169 if (!glyph)
21170 continue;
21171 font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
21172 if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
21173 {
21174 DebugNodeFontGlyph(font, glyph);
21175 EndTooltip();
21176 }
21177 }
21178 Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
21179 TreePop();
21180 }
21181 TreePop();
21182 }
21183 TreePop();
21184}
21185
21186void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph)
21187{
21188 Text("Codepoint: U+%04X", glyph->Codepoint);
21189 Separator();
21190 Text("Visible: %d", glyph->Visible);
21191 Text("AdvanceX: %.1f", glyph->AdvanceX);
21192 Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
21193 Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
21194}
21195
21196// [DEBUG] Display contents of ImGuiStorage
21197void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
21198{
21199 if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
21200 return;
21201 for (const ImGuiStorage::ImGuiStoragePair& p : storage->Data)
21202 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.
21203 TreePop();
21204}
21205
21206// [DEBUG] Display contents of ImGuiTabBar
21207void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
21208{
21209 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
21210 char buf[256];
21211 char* p = buf;
21212 const char* buf_end = buf + IM_ARRAYSIZE(buf);
21213 const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
21214 p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
21215 for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
21216 {
21217 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
21218 p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
21219 }
21220 p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
21221 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
21222 bool open = TreeNode(label, "%s", buf);
21223 if (!is_active) { PopStyleColor(); }
21224 if (is_active && IsItemHovered())
21225 {
21226 ImDrawList* draw_list = GetForegroundDrawList();
21227 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
21228 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));
21229 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));
21230 }
21231 if (open)
21232 {
21233 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
21234 {
21235 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
21236 PushID(tab);
21237 if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
21238 if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
21239 Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
21240 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
21241 PopID();
21242 }
21243 TreePop();
21244 }
21245}
21246
21247void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
21248{
21249 ImGuiContext& g = *GImGui;
21250 SetNextItemOpen(true, ImGuiCond_Once);
21251 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");
21252 if (IsItemHovered())
21253 g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
21254 if (open)
21255 {
21256 ImGuiWindowFlags flags = viewport->Flags;
21257 BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%",
21258 viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
21259 viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y,
21260 viewport->PlatformMonitor, viewport->DpiScale * 100.0f);
21261 if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } }
21262 BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags,
21263 //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard
21264 (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
21265 (flags & ImGuiViewportFlags_IsMinimized) ? " IsMinimized" : "",
21266 (flags & ImGuiViewportFlags_IsFocused) ? " IsFocused" : "",
21267 (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "",
21268 (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "",
21269 (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "",
21270 (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "",
21271 (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "",
21272 (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "",
21273 (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "",
21274 (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "",
21275 (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "",
21276 (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "");
21277 for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
21278 DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
21279 TreePop();
21280 }
21281}
21282
21283void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
21284{
21285 if (window == NULL)
21286 {
21287 BulletText("%s: NULL", label);
21288 return;
21289 }
21290
21291 ImGuiContext& g = *GImGui;
21292 const bool is_active = window->WasActive;
21293 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
21294 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
21295 const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
21296 if (!is_active) { PopStyleColor(); }
21297 if (IsItemHovered() && is_active)
21298 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
21299 if (!open)
21300 return;
21301
21302 if (window->MemoryCompacted)
21303 TextDisabled("Note: some memory buffers have been compacted/freed.");
21304
21305 if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
21306 g.DebugBreakInWindow = window->ID;
21307
21308 ImGuiWindowFlags flags = window->Flags;
21309 DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
21310 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);
21311 BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
21312 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
21313 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
21314 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
21315 BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId);
21316 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" : "");
21317 BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
21318 BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
21319 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
21320 {
21321 ImRect r = window->NavRectRel[layer];
21322 if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
21323 BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
21324 else
21325 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);
21326 DebugLocateItemOnHover(window->NavLastIds[layer]);
21327 }
21328 const ImVec2* pr = window->NavPreferredScoringPosRel;
21329 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
21330 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.
21331 BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
21332
21333 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);
21334 BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1);
21335 BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible);
21336 if (window->DockNode || window->DockNodeAsHost)
21337 DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode");
21338
21339 if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
21340 if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); }
21341 if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
21342 if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
21343 if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
21344 if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
21345 {
21346 for (ImGuiOldColumns& columns : window->ColumnsStorage)
21347 DebugNodeColumns(&columns);
21348 TreePop();
21349 }
21350 DebugNodeStorage(&window->StateStorage, "Storage");
21351 TreePop();
21352}
21353
21354void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
21355{
21356 if (settings->WantDelete)
21357 BeginDisabled();
21358 Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
21359 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
21360 if (settings->WantDelete)
21361 EndDisabled();
21362}
21363
21364void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
21365{
21366 if (!TreeNode(label, "%s (%d)", label, windows->Size))
21367 return;
21368 for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
21369 {
21370 PushID((*windows)[i]);
21371 DebugNodeWindow((*windows)[i], "Window");
21372 PopID();
21373 }
21374 TreePop();
21375}
21376
21377// FIXME-OPT: This is technically suboptimal, but it is simpler this way.
21378void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
21379{
21380 for (int i = 0; i < windows_size; i++)
21381 {
21382 ImGuiWindow* window = windows[i];
21383 if (window->ParentWindowInBeginStack != parent_in_begin_stack)
21384 continue;
21385 char buf[20];
21386 ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext);
21387 //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
21388 DebugNodeWindow(window, buf);
21389 Indent();
21390 DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
21391 Unindent();
21392 }
21393}
21394
21395//-----------------------------------------------------------------------------
21396// [SECTION] DEBUG LOG WINDOW
21397//-----------------------------------------------------------------------------
21398
21399void ImGui::DebugLog(const char* fmt, ...)
21400{
21401 va_list args;
21402 va_start(args, fmt);
21403 DebugLogV(fmt, args);
21404 va_end(args);
21405}
21406
21407void ImGui::DebugLogV(const char* fmt, va_list args)
21408{
21409 ImGuiContext& g = *GImGui;
21410 const int old_size = g.DebugLogBuf.size();
21411 g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
21412 g.DebugLogBuf.appendfv(fmt, args);
21413 g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
21414 if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
21415 IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
21416#ifdef IMGUI_ENABLE_TEST_ENGINE
21417 if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
21418 IMGUI_TEST_ENGINE_LOG("%s", g.DebugLogBuf.begin() + old_size);
21419#endif
21420}
21421
21422// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
21423static void SameLineOrWrap(const ImVec2& size)
21424{
21425 ImGuiContext& g = *GImGui;
21426 ImGuiWindow* window = g.CurrentWindow;
21427 ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
21428 if (window->ClipRect.Contains(ImRect(pos, pos + size)))
21429 ImGui::SameLine();
21430}
21431
21432static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
21433{
21434 ImGuiContext& g = *GImGui;
21435 ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
21436 SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
21437 if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
21438 {
21439 g.DebugLogAutoDisableFrames = 2;
21440 g.DebugLogAutoDisableFlags |= flags;
21441 }
21442 ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
21443}
21444
21445void ImGui::ShowDebugLogWindow(bool* p_open)
21446{
21447 ImGuiContext& g = *GImGui;
21448 if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))
21449 SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
21450 if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
21451 {
21452 End();
21453 return;
21454 }
21455
21456 ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
21457 CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
21458 SetItemTooltip("(except InputRouting which is spammy)");
21459
21460 ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
21461 ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
21462 ShowDebugLogFlag("Docking", ImGuiDebugLogFlags_EventDocking);
21463 ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
21464 ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
21465 ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
21466 ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
21467 //ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
21468 ShowDebugLogFlag("Viewport", ImGuiDebugLogFlags_EventViewport);
21469 ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
21470
21471 if (SmallButton("Clear"))
21472 {
21473 g.DebugLogBuf.clear();
21474 g.DebugLogIndex.clear();
21475 }
21476 SameLine();
21477 if (SmallButton("Copy"))
21478 SetClipboardText(g.DebugLogBuf.c_str());
21479 BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
21480
21481 const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
21482 g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
21483
21484 ImGuiListClipper clipper;
21485 clipper.Begin(g.DebugLogIndex.size());
21486 while (clipper.Step())
21487 for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
21488 {
21489 const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no);
21490 const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no);
21491 TextUnformatted(line_begin, line_end); // Display line
21492 ImRect text_rect = g.LastItemData.Rect;
21493 if (IsItemHovered())
21494 for (const char* p = line_begin; p <= line_end - 10; p++) // Search for 0x???????? identifiers
21495 {
21496 ImGuiID id = 0;
21497 if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1)
21498 continue;
21499 ImVec2 p0 = CalcTextSize(line_begin, p);
21500 ImVec2 p1 = CalcTextSize(p, p + 10);
21501 g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
21502 if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
21503 DebugLocateItemOnHover(id);
21504 p += 10;
21505 }
21506 }
21507 g.DebugLogFlags = backup_log_flags;
21508 if (GetScrollY() >= GetScrollMaxY())
21509 SetScrollHereY(1.0f);
21510 EndChild();
21511
21512 End();
21513}
21514
21515//-----------------------------------------------------------------------------
21516// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
21517//-----------------------------------------------------------------------------
21518
21519// Draw a small cross at current CursorPos in current window's DrawList
21520void ImGui::DebugDrawCursorPos(ImU32 col)
21521{
21522 ImGuiContext& g = *GImGui;
21523 ImGuiWindow* window = g.CurrentWindow;
21524 ImVec2 pos = window->DC.CursorPos;
21525 window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
21526 window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
21527}
21528
21529// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
21530void ImGui::DebugDrawLineExtents(ImU32 col)
21531{
21532 ImGuiContext& g = *GImGui;
21533 ImGuiWindow* window = g.CurrentWindow;
21534 float curr_x = window->DC.CursorPos.x;
21535 float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
21536 float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
21537 window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
21538 window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
21539 window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
21540}
21541
21542// Draw last item rect in ForegroundDrawList (so it is always visible)
21543void ImGui::DebugDrawItemRect(ImU32 col)
21544{
21545 ImGuiContext& g = *GImGui;
21546 ImGuiWindow* window = g.CurrentWindow;
21547 GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
21548}
21549
21550// [DEBUG] Locate item position/rectangle given an ID.
21551static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green
21552
21553void ImGui::DebugLocateItem(ImGuiID target_id)
21554{
21555 ImGuiContext& g = *GImGui;
21556 g.DebugLocateId = target_id;
21557 g.DebugLocateFrames = 2;
21558 g.DebugBreakInLocateId = false;
21559}
21560
21561// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
21562void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
21563{
21564 if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
21565 return;
21566 ImGuiContext& g = *GImGui;
21567 DebugLocateItem(target_id);
21568 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);
21569
21570 // Can't easily use a context menu here because it will mess with focus, active id etc.
21571 if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
21572 {
21573 DebugBreakButtonTooltip(false, "in ItemAdd()");
21574 if (IsKeyChordPressed(g.DebugBreakKeyChord))
21575 g.DebugBreakInLocateId = true;
21576 }
21577}
21578
21579void ImGui::DebugLocateItemResolveWithLastItem()
21580{
21581 ImGuiContext& g = *GImGui;
21582
21583 // [DEBUG] Debug break requested by user
21584 if (g.DebugBreakInLocateId)
21585 IM_DEBUG_BREAK();
21586
21587 ImGuiLastItemData item_data = g.LastItemData;
21588 g.DebugLocateId = 0;
21589 ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
21590 ImRect r = item_data.Rect;
21591 r.Expand(3.0f);
21592 ImVec2 p1 = g.IO.MousePos;
21593 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);
21594 draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
21595 draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
21596}
21597
21598void ImGui::DebugStartItemPicker()
21599{
21600 ImGuiContext& g = *GImGui;
21601 g.DebugItemPickerActive = true;
21602}
21603
21604// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
21605void ImGui::UpdateDebugToolItemPicker()
21606{
21607 ImGuiContext& g = *GImGui;
21608 g.DebugItemPickerBreakId = 0;
21609 if (!g.DebugItemPickerActive)
21610 return;
21611
21612 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
21613 SetMouseCursor(ImGuiMouseCursor_Hand);
21614 if (IsKeyPressed(ImGuiKey_Escape))
21615 g.DebugItemPickerActive = false;
21616 const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
21617 if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
21618 {
21619 g.DebugItemPickerBreakId = hovered_id;
21620 g.DebugItemPickerActive = false;
21621 }
21622 for (int mouse_button = 0; mouse_button < 3; mouse_button++)
21623 if (change_mapping && IsMouseClicked(mouse_button))
21624 g.DebugItemPickerMouseButton = (ImU8)mouse_button;
21625 SetNextWindowBgAlpha(0.70f);
21626 if (!BeginTooltip())
21627 return;
21628 Text("HoveredId: 0x%08X", hovered_id);
21629 Text("Press ESC to abort picking.");
21630 const char* mouse_button_names[] = { "Left", "Right", "Middle" };
21631 if (change_mapping)
21632 Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
21633 else
21634 TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
21635 EndTooltip();
21636}
21637
21638// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
21639void ImGui::UpdateDebugToolStackQueries()
21640{
21641 ImGuiContext& g = *GImGui;
21642 ImGuiIDStackTool* tool = &g.DebugIDStackTool;
21643
21644 // Clear hook when id stack tool is not visible
21645 g.DebugHookIdInfo = 0;
21646 if (g.FrameCount != tool->LastActiveFrame + 1)
21647 return;
21648
21649 // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
21650 // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
21651 const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
21652 if (tool->QueryId != query_id)
21653 {
21654 tool->QueryId = query_id;
21655 tool->StackLevel = -1;
21656 tool->Results.resize(0);
21657 }
21658 if (query_id == 0)
21659 return;
21660
21661 // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
21662 int stack_level = tool->StackLevel;
21663 if (stack_level >= 0 && stack_level < tool->Results.Size)
21664 if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
21665 tool->StackLevel++;
21666
21667 // Update hook
21668 stack_level = tool->StackLevel;
21669 if (stack_level == -1)
21670 g.DebugHookIdInfo = query_id;
21671 if (stack_level >= 0 && stack_level < tool->Results.Size)
21672 {
21673 g.DebugHookIdInfo = tool->Results[stack_level].ID;
21674 tool->Results[stack_level].QueryFrameCount++;
21675 }
21676}
21677
21678// [DEBUG] ID Stack tool: hooks called by GetID() family functions
21679void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
21680{
21681 ImGuiContext& g = *GImGui;
21682 ImGuiWindow* window = g.CurrentWindow;
21683 ImGuiIDStackTool* tool = &g.DebugIDStackTool;
21684
21685 // Step 0: stack query
21686 // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
21687 if (tool->StackLevel == -1)
21688 {
21689 tool->StackLevel++;
21690 tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
21691 for (int n = 0; n < window->IDStack.Size + 1; n++)
21692 tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
21693 return;
21694 }
21695
21696 // Step 1+: query for individual level
21697 IM_ASSERT(tool->StackLevel >= 0);
21698 if (tool->StackLevel != window->IDStack.Size)
21699 return;
21700 ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
21701 IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
21702
21703 switch (data_type)
21704 {
21705 case ImGuiDataType_S32:
21706 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
21707 break;
21708 case ImGuiDataType_String:
21709 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);
21710 break;
21711 case ImGuiDataType_Pointer:
21712 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
21713 break;
21714 case ImGuiDataType_ID:
21715 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.
21716 return;
21717 ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
21718 break;
21719 default:
21720 IM_ASSERT(0);
21721 }
21722 info->QuerySuccess = true;
21723 info->DataType = data_type;
21724}
21725
21726static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
21727{
21728 ImGuiStackLevelInfo* info = &tool->Results[n];
21729 ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
21730 if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
21731 return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
21732 if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
21733 return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
21734 if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
21735 return (*buf = 0);
21736#ifdef IMGUI_ENABLE_TEST_ENGINE
21737 if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
21738 return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
21739#endif
21740 return ImFormatString(buf, buf_size, "???");
21741}
21742
21743// ID Stack Tool: Display UI
21744void ImGui::ShowIDStackToolWindow(bool* p_open)
21745{
21746 ImGuiContext& g = *GImGui;
21747 if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))
21748 SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
21749 if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
21750 {
21751 End();
21752 return;
21753 }
21754
21755 // Display hovered/active status
21756 ImGuiIDStackTool* tool = &g.DebugIDStackTool;
21757 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
21758 const ImGuiID active_id = g.ActiveId;
21759#ifdef IMGUI_ENABLE_TEST_ENGINE
21760 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) : "");
21761#else
21762 Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id);
21763#endif
21764 SameLine();
21765 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.");
21766
21767 // CTRL+C to copy path
21768 const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
21769 Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC);
21770 SameLine();
21771 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*");
21772 if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
21773 {
21774 tool->CopyToClipboardLastTime = (float)g.Time;
21775 char* p = g.TempBuffer.Data;
21776 char* p_end = p + g.TempBuffer.Size;
21777 for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)
21778 {
21779 *p++ = '/';
21780 char level_desc[256];
21781 StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
21782 for (int n = 0; level_desc[n] && p + 2 < p_end; n++)
21783 {
21784 if (level_desc[n] == '/')
21785 *p++ = '\\';
21786 *p++ = level_desc[n];
21787 }
21788 }
21789 *p = '\0';
21790 SetClipboardText(g.TempBuffer.Data);
21791 }
21792
21793 // Display decorated stack
21794 tool->LastActiveFrame = g.FrameCount;
21795 if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
21796 {
21797 const float id_width = CalcTextSize("0xDDDDDDDD").x;
21798 TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
21799 TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
21800 TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
21801 TableHeadersRow();
21802 for (int n = 0; n < tool->Results.Size; n++)
21803 {
21804 ImGuiStackLevelInfo* info = &tool->Results[n];
21805 TableNextColumn();
21806 Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
21807 TableNextColumn();
21808 StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
21809 TextUnformatted(g.TempBuffer.Data);
21810 TableNextColumn();
21811 Text("0x%08X", info->ID);
21812 if (n == tool->Results.Size - 1)
21813 TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
21814 }
21815 EndTable();
21816 }
21817 End();
21818}
21819
21820#else
21821
21822void ImGui::ShowMetricsWindow(bool*) {}
21823void ImGui::ShowFontAtlas(ImFontAtlas*) {}
21824void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
21825void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
21826void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
21827void ImGui::DebugNodeFont(ImFont*) {}
21828void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
21829void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
21830void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
21831void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
21832void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
21833void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
21834
21835void ImGui::DebugLog(const char*, ...) {}
21836void ImGui::DebugLogV(const char*, va_list) {}
21837void ImGui::ShowDebugLogWindow(bool*) {}
21838void ImGui::ShowIDStackToolWindow(bool*) {}
21839void ImGui::DebugStartItemPicker() {}
21840void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
21841
21842#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
21843
21844//-----------------------------------------------------------------------------
21845
21846// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
21847// 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.
21848#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
21849#include "imgui_user.inl"
21850#endif
21851
21852//-----------------------------------------------------------------------------
21853
21854#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:1963
char * ImStrdupcpy(char *dst, size_t *p_dst_size, const char *src)
Definition imgui.cc:1934
#define va_copy(dest, src)
Definition imgui.cc:2807
char * ImStrdup(const char *str)
Definition imgui.cc:1927
ImGuiContext * GImGui
Definition imgui.cc:1224
int ImFormatStringV(char *buf, size_t buf_size, const char *fmt, va_list args)
Definition imgui.cc:2063
ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2 &p1, const ImVec2 &p2, const ImVec2 &p3, const ImVec2 &p4, const ImVec2 &p, float tess_tol)
Definition imgui.cc:1841
const ImWchar * ImStrbolW(const ImWchar *buf_mid_line, const ImWchar *buf_begin)
Definition imgui.cc:1969
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:1872
void ImFormatStringToTempBuffer(const char **out_buf, const char **out_buf_end, const char *fmt,...)
Definition imgui.cc:2079
int ImTextStrToUtf8(char *out_buf, int out_buf_size, const ImWchar *in_text, const ImWchar *in_text_end)
Definition imgui.cc:2416
int ImStrnicmp(const char *str1, const char *str2, size_t count)
Definition imgui.cc:1911
int ImStrlenW(const ImWchar *str)
Definition imgui.cc:1954
const char * ImStrchrRange(const char *str, const char *str_end, char c)
Definition imgui.cc:1948
int ImFormatString(char *buf, size_t buf_size, const char *fmt,...)
Definition imgui.cc:2045
ImVec2 ImLineClosestPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &p)
Definition imgui.cc:1851
void ImStrTrimBlanks(char *buf)
Definition imgui.cc:1999
void ImFormatStringToTempBufferV(const char **out_buf, const char **out_buf_end, const char *fmt, va_list args)
Definition imgui.cc:2087
IM_MSVC_RUNTIME_CHECKS_RESTORE IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
Definition imgui.cc:2478
const char * ImStrSkipBlank(const char *str)
Definition imgui.cc:2014
const char * ImTextCharToUtf8(char out_buf[5], unsigned int c)
Definition imgui.cc:2393
bool ImTriangleContainsPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
Definition imgui.cc:1864
ImU64 ImFileWrite(const void *data, ImU64 sz, ImU64 count, ImFileHandle f)
Definition imgui.cc:2223
#define IMGUI_DEBUG_NAV_SCORING
Definition imgui.cc:1091
int ImTextCountUtf8BytesFromStr(const ImWchar *in_text, const ImWchar *in_text_end)
Definition imgui.cc:2432
ImU64 ImFileRead(void *data, ImU64 sz, ImU64 count, ImFileHandle f)
Definition imgui.cc:2222
ImGuiID ImHashStr(const char *data_p, size_t data_size, ImGuiID seed)
Definition imgui.cc:2160
const char * ImTextFindPreviousUtf8Codepoint(const char *in_text_start, const char *in_text_curr)
Definition imgui.cc:2446
IM_MSVC_RUNTIME_CHECKS_OFF int ImTextCharFromUtf8(unsigned int *out_char, const char *in_text, const char *in_text_end)
Definition imgui.cc:2277
int ImTextCountCharsFromUtf8(const char *in_text, const char *in_text_end)
Definition imgui.cc:2345
ImGuiDockRequestType
Definition imgui.cc:15853
@ ImGuiDockRequestType_Split
Definition imgui.cc:15857
@ ImGuiDockRequestType_Dock
Definition imgui.cc:15855
@ ImGuiDockRequestType_None
Definition imgui.cc:15854
@ ImGuiDockRequestType_Undock
Definition imgui.cc:15856
bool ImFileClose(ImFileHandle f)
Definition imgui.cc:2220
void * ImFileLoadToMemory(const char *filename, const char *mode, size_t *out_file_size, int padding_bytes)
Definition imgui.cc:2229
const char * ImStristr(const char *haystack, const char *haystack_end, const char *needle, const char *needle_end)
Definition imgui.cc:1976
ImVec2 ImBezierCubicClosestPoint(const ImVec2 &p1, const ImVec2 &p2, const ImVec2 &p3, const ImVec2 &p4, const ImVec2 &p, int num_segments)
Definition imgui.cc:1783
ImGuiID ImHashData(const void *data_p, size_t data_size, ImGuiID seed)
Definition imgui.cc:2144
ImU64 ImFileGetSize(ImFileHandle f)
Definition imgui.cc:2221
int ImStricmp(const char *str1, const char *str2)
Definition imgui.cc:1904
int ImTextCountUtf8BytesFromChar(const char *in_text, const char *in_text_end)
Definition imgui.cc:2401
ImFileHandle ImFileOpen(const char *filename, const char *mode)
Definition imgui.cc:2195
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:2329
void ImStrncpy(char *dst, const char *src, size_t count)
Definition imgui.cc:1918
ImVec2 ImTriangleClosestPoint(const ImVec2 &a, const ImVec2 &b, const ImVec2 &c, const ImVec2 &p)
Definition imgui.cc:1883
int ImTextCountLines(const char *in_text, const char *in_text_end)
Definition imgui.cc:2457
#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:20116
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:37
const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID
Definition imgui.cc:1183
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:24
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
Definition lz4.cc:146
constexpr double e
Definition Math.hh:21
T length(const vecN< N, T > &x)
Definition gl_vec.hh:376
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition gl_vec.hh:302
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 Indent(float indent_w, std::invocable<> auto next)
Definition ImGuiCpp.hh:224
bool Checkbox(const HotKey &hotKey, BooleanSetting &setting)
Definition ImGuiUtils.cc:81
bool SliderInt(IntegerSetting &setting, ImGuiSliderFlags flags)
bool InputText(Setting &setting)
auto remove(ForwardRange &&range, const T &value)
Definition ranges.hh:291
size_t size(std::string_view utf8)
auto distance(octet_iterator first, octet_iterator last)
signed char SplitAxis
Definition imgui.cc:15905
ImGuiDockNodeFlags Flags
Definition imgui.cc:15907
ImGuiDockNode * CentralNode
Definition imgui.cc:16911
ImGuiDockNode * FirstNodeWithWindows
Definition imgui.cc:16912
ImGuiDockNode * SplitNode
Definition imgui.cc:15890
ImRect DropRectsDraw[ImGuiDir_COUNT+1]
Definition imgui.cc:15893
ImGuiDockNode FutureNode
Definition imgui.cc:15885
ImGuiDockNode * UndockTargetNode
Definition imgui.cc:15870
ImGuiWindow * DockPayload
Definition imgui.cc:15865
ImGuiDir DockSplitDir
Definition imgui.cc:15866
ImGuiDockNode * DockTargetNode
Definition imgui.cc:15864
ImGuiWindow * UndockTargetWindow
Definition imgui.cc:15869
ImGuiDockRequestType Type
Definition imgui.cc:15862
ImGuiWindow * DockTargetWindow
Definition imgui.cc:15863
float DockSplitRatio
Definition imgui.cc:15867
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition xrange.hh:147