Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/imgui/src/imgui.cpp
7362 views
1
// dear imgui, v1.92.6 WIP
2
// (main code and documentation)
3
4
// Help:
5
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
6
// - Read top of imgui.cpp for more details, links and comments.
7
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including imgui.h (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
8
9
// Resources:
10
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
11
// - Homepage ................... https://github.com/ocornut/imgui
12
// - Releases & Changelog ....... https://github.com/ocornut/imgui/releases
13
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
14
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
15
// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
16
// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
17
// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings + backends for various tech/engines)
18
// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
19
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
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
// - Web version of the Demo .... https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html (w/ source code browser)
24
25
// For FIRST-TIME users having issues compiling/linking/running:
26
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
27
// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
28
// Since 1.92, we encourage font loading questions to also be posted in 'Issues'.
29
30
// Copyright (c) 2014-2026 Omar Cornut
31
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
32
// See LICENSE.txt for copyright and licensing details (standard MIT License).
33
// This library is free but needs your support to sustain development and maintenance.
34
// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
35
// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
36
// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
37
38
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
39
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
40
// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
41
// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
42
// to a better solution or official support for them.
43
44
/*
45
46
Index of this file:
47
48
DOCUMENTATION
49
50
- MISSION STATEMENT
51
- CONTROLS GUIDE
52
- PROGRAMMER GUIDE
53
- READ FIRST
54
- HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
55
- GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
56
- HOW A SIMPLE APPLICATION MAY LOOK LIKE
57
- USING CUSTOM BACKEND / CUSTOM ENGINE
58
- API BREAKING CHANGES (read me when you update!)
59
- FREQUENTLY ASKED QUESTIONS (FAQ)
60
- Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
61
62
CODE
63
(search for "[SECTION]" in the code to find them)
64
65
// [SECTION] INCLUDES
66
// [SECTION] FORWARD DECLARATIONS
67
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
68
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
69
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
70
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
71
// [SECTION] MISC HELPERS/UTILITIES (File functions)
72
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
73
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
74
// [SECTION] ImGuiStorage
75
// [SECTION] ImGuiTextFilter
76
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
77
// [SECTION] ImGuiListClipper
78
// [SECTION] STYLING
79
// [SECTION] RENDER HELPERS
80
// [SECTION] INITIALIZATION, SHUTDOWN
81
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
82
// [SECTION] FONTS, TEXTURES
83
// [SECTION] ID STACK
84
// [SECTION] INPUTS
85
// [SECTION] ERROR CHECKING, STATE RECOVERY
86
// [SECTION] ITEM SUBMISSION
87
// [SECTION] LAYOUT
88
// [SECTION] SCROLLING
89
// [SECTION] TOOLTIPS
90
// [SECTION] POPUPS
91
// [SECTION] WINDOW FOCUS
92
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
93
// [SECTION] DRAG AND DROP
94
// [SECTION] LOGGING/CAPTURING
95
// [SECTION] SETTINGS
96
// [SECTION] LOCALIZATION
97
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
98
// [SECTION] PLATFORM DEPENDENT HELPERS
99
// [SECTION] METRICS/DEBUGGER WINDOW
100
// [SECTION] DEBUG LOG WINDOW
101
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
102
103
*/
104
105
//-----------------------------------------------------------------------------
106
// DOCUMENTATION
107
//-----------------------------------------------------------------------------
108
109
/*
110
111
MISSION STATEMENT
112
=================
113
114
- Easy to use to create code-driven and data-driven tools.
115
- Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
116
- Easy to hack and improve.
117
- Minimize setup and maintenance.
118
- Minimize state storage on user side.
119
- Minimize state synchronization.
120
- Portable, minimize dependencies, run on target (consoles, phones, etc.).
121
- Efficient runtime and memory consumption.
122
123
Designed primarily for developers and content-creators, not the typical end-user!
124
Some of the current weaknesses (which we aim to address in the future) includes:
125
126
- Doesn't look fancy by default.
127
- Limited layout features, intricate layouts are typically crafted in code.
128
129
130
CONTROLS GUIDE
131
==============
132
133
- MOUSE CONTROLS
134
- Mouse wheel: Scroll vertically.
135
- Shift+Mouse wheel: Scroll horizontally.
136
- Click [X]: Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
137
- Click ^, Double-Click title: Collapse window.
138
- Drag on corner/border: Resize window (double-click to auto fit window to its contents).
139
- Drag on any empty space: Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
140
- Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack).
141
142
- TEXT EDITOR
143
- Hold Shift or Drag Mouse: Select text.
144
- Ctrl+Left/Right: Word jump.
145
- Ctrl+Shift+Left/Right: Select words.
146
- Ctrl+A or Double-Click: Select All.
147
- Ctrl+X, Ctrl+C, Ctrl+V: Use OS clipboard.
148
- Ctrl+Z Undo.
149
- Ctrl+Y or Ctrl+Shift+Z: Redo.
150
- ESCAPE: Revert text to its original value.
151
- On macOS, controls are automatically adjusted to match standard macOS text editing and behaviors.
152
(for 99% of shortcuts, Ctrl is replaced by Cmd on macOS).
153
154
- KEYBOARD CONTROLS
155
- Basic:
156
- Tab, Shift+Tab Cycle through text editable fields.
157
- Ctrl+Tab, Ctrl+Shift+Tab Cycle through windows.
158
- Ctrl+Click Input text into a Slider or Drag widget.
159
- Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
160
- Tab, Shift+Tab: Cycle through every items.
161
- Arrow keys Move through items using directional navigation. Tweak value.
162
- Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys).
163
- Enter Activate item (prefer text input when possible).
164
- Space Activate item (prefer tweaking with arrows when possible).
165
- Escape Deactivate item, leave child window, close popup.
166
- Page Up, Page Down Previous page, next page.
167
- Home, End Scroll to top, scroll to bottom.
168
- Alt Toggle between scrolling layer and menu layer.
169
- Ctrl+Tab then Ctrl+Arrows Move window. Hold Shift to resize instead of moving.
170
- Output when ImGuiConfigFlags_NavEnableKeyboard set,
171
- io.WantCaptureKeyboard flag is set when keyboard is claimed.
172
- io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
173
- io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
174
175
- GAMEPAD CONTROLS
176
- Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
177
- Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
178
- Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
179
- Backend support: backend needs to:
180
- Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
181
- For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
182
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.).
183
- If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
184
with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
185
186
- REMOTE INPUTS SHARING & MOUSE EMULATION
187
- PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
188
- 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)
189
in order to share your PC mouse/keyboard.
190
- See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
191
- On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.
192
Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
193
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.
194
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.
195
(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!)
196
(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
197
to set a boolean to ignore your other external mouse positions until the external source is moved again.)
198
199
200
PROGRAMMER GUIDE
201
================
202
203
READ FIRST
204
----------
205
- Remember to check the wonderful Wiki: https://github.com/ocornut/imgui/wiki
206
- Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
207
The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
208
data retention on your side, less state duplication, less state synchronization, fewer bugs.
209
- Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
210
Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
211
- The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
212
- Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
213
You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
214
- Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
215
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,
216
where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
217
- Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
218
- Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
219
If you get an assert, read the messages and comments around the assert.
220
- This codebase aims to be highly optimized:
221
- A typical idle frame should never call malloc/free.
222
- We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
223
- We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
224
Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
225
- This codebase aims to be both highly opinionated and highly flexible:
226
- This code works because of the things it choose to solve or not solve.
227
- C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
228
and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
229
This is to increase compatibility, increase maintainability and facilitate use from other languages.
230
- C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
231
See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
232
We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
233
- C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
234
(so don't use ImVector in your code or at our own risk!).
235
- Building: We don't use nor mandate a build system for the main library.
236
This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
237
This is also because providing a build system for the main library would be of little-value.
238
The build problems are almost never coming from the main library but from specific backends.
239
240
241
HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
242
----------------------------------------------
243
- Update submodule or copy/overwrite every file.
244
- About imconfig.h:
245
- You may modify your copy of imconfig.h, in this case don't overwrite it.
246
- or you may locally branch to modify imconfig.h and merge/rebase latest.
247
- or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
248
specify a custom path for your imconfig.h file and instead not have to modify the default one.
249
250
- Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
251
- Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
252
- You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
253
- Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
254
If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
255
from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
256
likely be a comment about it. Please report any issue to the GitHub page!
257
- To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
258
- Try to keep your copy of Dear ImGui reasonably up to date!
259
260
261
GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
262
---------------------------------------------------------------
263
- See https://github.com/ocornut/imgui/wiki/Getting-Started.
264
- Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
265
- In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
266
- Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
267
It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
268
- 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.
269
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
270
- Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
271
Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
272
phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
273
- Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
274
- 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.
275
276
277
HOW A SIMPLE APPLICATION MAY LOOK LIKE
278
--------------------------------------
279
280
USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
281
The sub-folders in examples/ contain examples applications following this structure.
282
283
// Application init: create a dear imgui context, setup some options, load fonts
284
ImGui::CreateContext();
285
ImGuiIO& io = ImGui::GetIO();
286
// TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
287
// TODO: Fill optional fields of the io structure later.
288
// TODO: Load TTF/OTF fonts if you don't want to use the default font.
289
290
// Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
291
ImGui_ImplWin32_Init(hwnd);
292
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
293
294
// Application main loop
295
while (true)
296
{
297
// Feed inputs to dear imgui, start new frame
298
ImGui_ImplDX11_NewFrame();
299
ImGui_ImplWin32_NewFrame();
300
ImGui::NewFrame();
301
302
// Any application code here
303
ImGui::Text("Hello, world!");
304
305
// Render dear imgui into framebuffer
306
ImGui::Render();
307
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
308
g_pSwapChain->Present(1, 0);
309
}
310
311
// Shutdown
312
ImGui_ImplDX11_Shutdown();
313
ImGui_ImplWin32_Shutdown();
314
ImGui::DestroyContext();
315
316
To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
317
you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
318
Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
319
320
321
USING CUSTOM BACKEND / CUSTOM ENGINE
322
------------------------------------
323
324
IMPLEMENTING YOUR PLATFORM BACKEND:
325
-> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for basic instructions.
326
-> the Platform backends in impl_impl_XXX.cpp files contain many implementations.
327
328
IMPLEMENTING YOUR RenderDrawData() function:
329
-> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md
330
-> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_RenderDrawData() function.
331
332
IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
333
-> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md
334
-> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_UpdateTexture() function.
335
336
Basic application/backend skeleton:
337
338
// Application init: create a Dear ImGui context, setup some options, load fonts
339
ImGui::CreateContext();
340
ImGuiIO& io = ImGui::GetIO();
341
// TODO: set io.ConfigXXX values, e.g.
342
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard controls
343
344
// TODO: Load TTF/OTF fonts if you don't want to use the default font.
345
io.Fonts->AddFontFromFileTTF("NotoSans.ttf");
346
347
// Application main loop
348
while (true)
349
{
350
// Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
351
// (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
352
io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
353
io.DisplaySize.x = 1920.0f; // set the current display width
354
io.DisplaySize.y = 1280.0f; // set the current display height here
355
io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position
356
io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states
357
io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states
358
359
// Call NewFrame(), after this point you can use ImGui::* functions anytime
360
// (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
361
ImGui::NewFrame();
362
363
// Most of your application code here
364
ImGui::Text("Hello, world!");
365
MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
366
MyGameRender(); // may use any Dear ImGui functions as well!
367
368
// End the dear imgui frame
369
// (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)
370
ImGui::EndFrame(); // this is automatically called by Render(), but available
371
ImGui::Render();
372
373
// Update textures
374
ImDrawData* draw_data = ImGui::GetDrawData();
375
for (ImTextureData* tex : *draw_data->Textures)
376
if (tex->Status != ImTextureStatus_OK)
377
MyImGuiBackend_UpdateTexture(tex);
378
379
// Render dear imgui contents, swap buffers
380
MyImGuiBackend_RenderDrawData(draw_data);
381
SwapBuffers();
382
}
383
384
// Shutdown
385
ImGui::DestroyContext();
386
387
388
389
API BREAKING CHANGES
390
====================
391
392
Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
393
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.
394
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.
395
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
396
397
- 2025/12/23 (1.92.6) - Fonts:AddFontDefault() now automatically selects an embedded font between the new scalable AddFontDefaultVector() and the classic pixel-clean AddFontDefaultBitmap().
398
The default selection is based on (style.FontSizeBase * FontScaleMain * FontScaleDpi) reaching a small threshold. Prefer calling either based on your own logic. You can call AddFontDefaultBitmap() to ensure legacy behavior.
399
- 2025/12/23 (1.92.6) - Fonts: removed ImFontConfig::PixelSnapV added in 1.92 which turns out is unnecessary (and misdocumented). Post-rescale GlyphOffset is always rounded.
400
- 2025/12/17 (1.92.6) - Renamed helper macro IM_ARRAYSIZE() -> IM_COUNTOF(). Kept redirection/legacy name for now.
401
- 2025/12/11 (1.92.6) - Hashing: handling of "###" operator to reset to seed within a string identifier doesn't include the "###" characters in the output hash anymore.
402
- Before: GetID("Hello###World") == GetID("###World") != GetID("World");
403
- Now: GetID("Hello###World") == GetID("###World") == GetID("World");
404
- This has the property of facilitating concatenating and manipulating identifiers using "###", and will allow fixing other dangling issues.
405
- This will invalidate hashes (stored in .ini data) for Tables and Windows that are using the "###" operators. (#713, #1698)
406
- 2025/11/24 (1.92.6) - Fonts: Fixed handling of `ImFontConfig::FontDataOwnedByAtlas = false` which did erroneously make a copy of the font data, essentially defeating the purpose of this flag and wasting memory.
407
(trivia: undetected since July 2015, this is perhaps the oldest bug in Dear ImGui history, albeit for a rarely used feature, see #9086)
408
HOWEVER, fixing this bug is likely to surface bugs in user code using `FontDataOwnedByAtlas = false`.
409
- Prior to 1.92, font data only needed to be available during the atlas->AddFontXXX() call.
410
- Since 1.92, font data needs to available until atlas->RemoveFont(), or more typically until a shutdown of the owning context or font atlas.
411
- The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether.
412
- 2025/11/06 (1.92.5) - BeginChild: commented out some legacy names which were obsoleted in 1.90.0 (Nov 2023), 1.90.9 (July 2024), 1.91.1 (August 2024):
413
- ImGuiChildFlags_Border --> ImGuiChildFlags_Borders
414
- ImGuiWindowFlags_NavFlattened --> ImGuiChildFlags_NavFlattened (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_NavFlattened) --> BeginChild(name, size, ImGuiChildFlags_NavFlattened, 0)
415
- ImGuiWindowFlags_AlwaysUseWindowPadding --> ImGuiChildFlags_AlwaysUseWindowPadding (moved to ImGuiChildFlags). BeginChild(name, size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding) --> BeginChild(name, size, ImGuiChildFlags_AlwaysUseWindowPadding, 0)
416
- 2025/11/06 (1.92.5) - Keys: commented out legacy names which were obsoleted in 1.89.0 (August 2022):
417
- ImGuiKey_ModCtrl --> ImGuiMod_Ctrl
418
- ImGuiKey_ModShift --> ImGuiMod_Shift
419
- ImGuiKey_ModAlt --> ImGuiMod_Alt
420
- ImGuiKey_ModSuper --> ImGuiMod_Super
421
- 2025/11/06 (1.92.5) - IO: commented out legacy io.ClearInputCharacters() obsoleted in 1.89.8 (Aug 2023). Calling io.ClearInputKeys() is enough.
422
- 2025/11/06 (1.92.5) - Commented out legacy SetItemAllowOverlap() obsoleted in 1.89.7: this never worked right. Use SetNextItemAllowOverlap() _before_ item instead.
423
- 2025/10/14 (1.92.4) - TreeNode, Selectable, Clipper: commented out legacy names which were obsoleted in 1.89.7 (July 2023) and 1.89.9 (Sept 2023);
424
- ImGuiTreeNodeFlags_AllowItemOverlap --> ImGuiTreeNodeFlags_AllowOverlap
425
- ImGuiSelectableFlags_AllowItemOverlap --> ImGuiSelectableFlags_AllowOverlap
426
- ImGuiListClipper::IncludeRangeByIndices() --> ImGuiListClipper::IncludeItemsByIndex()
427
- 2025/08/08 (1.92.2) - Backends: SDL_GPU3: Changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, which is more natural and easier for user to manage. If you need to change the current sampler, you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988)
428
- 2025/07/31 (1.92.2) - Tabs: Renamed ImGuiTabBarFlags_FittingPolicyResizeDown to ImGuiTabBarFlags_FittingPolicyShrink. Kept inline redirection enum (will obsolete).
429
- 2025/06/25 (1.92.0) - Layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM
430
to extend parent window/cell boundaries. Replaced with assert/tooltip that would already happens if previously using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#5548, #4510, #3355, #1760, #1490, #4152, #150)
431
- Incorrect way to make a window content size 200x200:
432
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
433
- Correct ways to make a window content size 200x200:
434
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
435
Begin(...) + Dummy(ImVec2(200,200)) + End();
436
- TL;DR; if the assert triggers, you can add a Dummy({0,0}) call to validate extending parent boundaries.
437
- 2025/06/11 (1.92.0) - THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015! I TRIED REALLY HARD TO KEEP THEM TO A MINIMUM, REDUCE THE AMOUNT OF INTERFERENCES, BUT INEVITABLY SOME USERS WILL BE AFFECTED.
438
IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS, PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO: https://github.com/ocornut/imgui/issues/
439
As part of the plan to reduce impact of API breaking changes, several unfinished changes/features/refactors related to font and text systems and scaling will be part of subsequent releases (1.92.1+).
440
If you are updating from an old version, and expecting a massive or difficult update, consider first updating to 1.91.9 to reduce the amount of changes.
441
- Hard to read? Refer to 'docs/Changelog.txt' for a less compact and more complete version of this!
442
- Fonts: **IMPORTANT**: if your app was solving the OSX/iOS Retina screen specific logical vs display scale problem by setting io.DisplayFramebufferScale (e.g. to 2.0f) + setting io.FontGlobalScale (e.g. to 1.0f/2.0f) + loading fonts at scaled sizes (e.g. size X * 2.0f):
443
This WILL NOT map correctly to the new system! Because font will rasterize as requested size.
444
- With a legacy backend (< 1.92): Instead of setting io.FontGlobalScale = 1.0f/N -> set ImFontCfg::RasterizerDensity = N. This already worked before, but is now pretty much required.
445
- With a new backend (1.92+): This should be all automatic. FramebufferScale is automatically used to set current font RasterizerDensity. FramebufferScale is a per-viewport property provided by backend through the Platform_GetWindowFramebufferScale() handler in 'docking' branch.
446
- Fonts: **IMPORTANT** on Font Sizing: Before 1.92, fonts were of a single size. They can now be dynamically sized.
447
- PushFont() API now has a REQUIRED size parameter.
448
- Before 1.92: PushFont() always used font "default" size specified in AddFont() call. It is equivalent to calling PushFont(font, font->LegacySize).
449
- Since 1.92: PushFont(font, 0.0f) preserve the current font size which is a shared value.
450
- To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site.
451
- Kept inline single parameter function. Will obsolete.
452
- Fonts: **IMPORTANT** on Font Merging:
453
- When searching for a glyph in multiple merged fonts: we search for the FIRST font source which contains the desired glyph.
454
Because the user doesn't need to provide glyph ranges any more, it is possible that a glyph that you expected to fetch from a secondary/merged icon font may be erroneously fetched from the primary font.
455
- When searching for a glyph in multiple merged fonts: we now search for the FIRST font source which contains the desired glyph. This is technically a different behavior than before!
456
- e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1.
457
After the update and when using a new backend, those glyphs may now loaded from Font Source 1!
458
- We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source:
459
// Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range
460
static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
461
ImFontConfig cfg1;
462
cfg1.GlyphExcludeRanges = exclude_ranges;
463
io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1);
464
// Add Font Source 2, which expects to use the range above
465
ImFontConfig cfg2;
466
cfg2.MergeMode = true;
467
io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2);
468
- You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate understanding which font input is providing which glyph.
469
- Fonts: **IMPORTANT** on Thread Safety:
470
- A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even thou we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded.
471
- Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont().
472
- Fonts: Removed support for PushFont(NULL) which was a shortcut for "default font".
473
- Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'.
474
- Textures: all API functions taking a 'ImTextureID' parameter are now taking a 'ImTextureRef'. Affected functions are: ImGui::Image(), ImGui::ImageWithBg(), ImGui::ImageButton(), ImDrawList::AddImage(), ImDrawList::AddImageQuad(), ImDrawList::AddImageRounded().
475
- Fonts: obsoleted ImFontAtlas::GetTexDataAsRGBA32(), GetTexDataAsAlpha8(), Build(), SetTexID(), IsBuilt() functions. The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete).
476
- Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling.
477
- Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese().
478
- Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327)
479
- Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't.
480
- Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors.
481
- Fonts: obsoleted ImFont::Scale which is not useful anymore.
482
- Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things:
483
- ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef.
484
- ImFontAtlas::TexID has been changed to ImFontAtlas::TexRef.
485
- ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]
486
- ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount.
487
- Each ImFont has a number of ImFontBaked instances corresponding to actively used sizes. ImFont::GetFontBaked(size) retrieves the one for a given size.
488
- Fields moved from ImFont to ImFontBaked: IndexAdvanceX[], Glyphs[], Ascent, Descent, FindGlyph(), FindGlyphNoFallback(), GetCharAdvance().
489
- Fields moved from ImFontAtlas to ImFontAtlas->Tex: ImFontAtlas::TexWidth => TexData->Width, ImFontAtlas::TexHeight => TexData->Height, ImFontAtlas::TexPixelsAlpha8/TexPixelsRGBA32 => TexData->GetPixels().
490
- Widget code may use ImGui::GetFontBaked() instead of ImGui::GetFont() to access font data for current font at current font size (and you may use font->GetFontBaked(size) to access it for any other size.)
491
- Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags.
492
If you used runtime imgui_freetype selection rather than the default IMGUI_ENABLE_FREETYPE compile-time option: Renamed/reworked ImFontBuilderIO into ImFontLoader. Renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader().
493
- old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()
494
- new: io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader()
495
- new: io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) to change dynamically at runtime [from 1.92.1]
496
- Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV().
497
- The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h.
498
- old:
499
const ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(custom_rect_id);
500
ImVec2 uv0, uv1;
501
atlas->GetCustomRectUV(r, &uv0, &uv1);
502
ImGui::Image(atlas->TexRef, ImVec2(r->w, r->h), uv0, uv1);
503
- new;
504
ImFontAtlasRect r;
505
atlas->GetCustomRect(custom_rect_id, &r);
506
ImGui::Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);
507
- We added a redirecting typedef but haven't attempted to magically redirect the field names, as this API is rarely used and the fix is simple.
508
- Obsoleted AddCustomRectFontGlyph() as the API does not make sense for scalable fonts. Kept existing function which uses the font "default size" (Sources[0]->LegacySize). Added a helper AddCustomRectFontGlyphForSize() which is immediately marked obsolete, but can facilitate transitioning old code.
509
- Prefer adding a font source (ImFontConfig) using a custom/procedural loader.
510
- DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture().
511
- Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() for all backends that had them. They should not be necessary any more.
512
- 2025/05/23 (1.92.0) - Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition()
513
- old: const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, ....);
514
- new: const char* ImFont::CalcWordWrapPosition (float size, const char* text, ....);
515
The leading 'float scale' parameters was changed to 'float size'. This was necessary as 'scale' is assuming standard font size which is a concept we aim to eliminate in an upcoming update. Kept inline redirection function.
516
- 2025/05/15 (1.92.0) - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete).
517
- 2025/05/15 (1.92.0) - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4. Use PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop)/PopItemFlag() instead. (#3092)
518
- 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead.
519
- 2025/03/05 (1.91.9) - BeginMenu(): Internals: reworked mangling of menu windows to use "###Menu_00" etc. instead of "##Menu_00", allowing them to also store the menu name before it. This shouldn't affect code unless directly accessing menu window from their mangled name.
520
- 2025/04/16 (1.91.9) - Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387)
521
- 2025/02/27 (1.91.9) - Image(): removed 'tint_col' and 'border_col' parameter from Image() function. Added ImageWithBg() replacement. (#8131, #8238)
522
- old: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 tint_col = (1,1,1,1), ImVec4 border_col = (0,0,0,0));
523
- new: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1));
524
- new: void ImageWithBg(ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 bg_col = (0,0,0,0), ImVec4 tint_col = (1,1,1,1));
525
- TL;DR: 'border_col' had misleading side-effect on layout, 'bg_col' was missing, parameter order couldn't be consistent with ImageButton().
526
- new behavior always use ImGuiCol_Border color + style.ImageBorderSize / ImGuiStyleVar_ImageBorderSize.
527
- old behavior altered border size (and therefore layout) based on border color's alpha, which caused variety of problems + old behavior a fixed 1.0f for border size which was not tweakable.
528
- kept legacy signature (will obsolete), which mimics the old behavior, but uses Max(1.0f, style.ImageBorderSize) when border_col is specified.
529
- added ImageWithBg() function which has both 'bg_col' (which was missing) and 'tint_col'. It was impossible to add 'bg_col' to Image() with a parameter order consistent with other functions, so we decided to remove 'tint_col' and introduce ImageWithBg().
530
- 2025/02/25 (1.91.9) - internals: fonts: ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]. ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourcesCount.
531
- 2025/02/06 (1.91.9) - renamed ImFontConfig::GlyphExtraSpacing.x to ImFontConfig::GlyphExtraAdvanceX.
532
- 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior.
533
prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior.
534
the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls:
535
- 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937)
536
- 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions.
537
As a result, old .ini data may be partially lost (docking and tables information particularly).
538
Because some users have crafted and storing .ini data as a way to workaround limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' compile-time option to keep using old CRC32 tables if you cannot afford invalidating old .ini data.
539
- 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)
540
- io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
541
- io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
542
- GetKeyIndex() is removed (obsoleted March 2022). The indirection is now unnecessary.
543
- pre-1.87 backends are not supported:
544
- backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
545
- backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
546
- for more reference:
547
- read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
548
- read https://github.com/ocornut/imgui/issues/4921
549
- if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
550
- obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
551
- fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements.
552
- 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
553
- 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
554
moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
555
kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!
556
- 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
557
this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.
558
you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
559
in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
560
- May warn: ImGui::Image((void*)MyTextureData, ...);
561
- May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
562
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);
563
- note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
564
- 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
565
- drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
566
although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
567
- 2024/09/10 (1.91.2) - internals: using multiple overlaid ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
568
it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
569
- 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
570
- 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
571
- io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
572
- io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line.
573
- io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660)
574
- io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
575
- io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
576
- access those via GetPlatformIO() instead of GetIO().
577
some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol.
578
- commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:
579
- old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID)
580
- new ImageButton() since 1.89 requires an explicit 'const char* str_id'
581
- old ImageButton() before 1.89 had frame_padding' override argument.
582
- new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().
583
- 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
584
you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
585
- instead of: GetWindowContentRegionMax().x - GetCursorPos().x
586
- you can use: GetContentRegionAvail().x
587
- instead of: GetWindowContentRegionMax().x + GetWindowPos().x
588
- you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
589
- instead of: GetContentRegionMax()
590
- you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
591
- instead of: GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
592
- you can use: GetContentRegionAvail() // when called from left edge of window
593
- 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
594
(internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
595
- 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
596
- 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
597
- commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
598
- ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
599
- 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
600
- old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
601
- new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
602
- 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow.
603
- old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
604
- new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
605
- 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
606
- 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
607
- 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
608
- 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
609
- 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library.
610
- 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
611
- 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
612
- old: CaptureKeyboardFromApp(bool)
613
- new: SetNextFrameWantCaptureKeyboard(bool)
614
- old: CaptureMouseFromApp(bool)
615
- new: SetNextFrameWantCaptureMouse(bool)
616
- 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).
617
- inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
618
- inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
619
- old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
620
- new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
621
- inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
622
- old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
623
- new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
624
- old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
625
- new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
626
for various reasons those changes makes sense. They are being made because making some of those API public.
627
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.
628
- 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.
629
- it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
630
- 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)
631
- 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
632
- 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
633
- 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).
634
- new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
635
with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
636
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.
637
(Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
638
- 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)
639
- 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)
640
- IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
641
- 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
642
- 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
643
- 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
644
- 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.
645
- 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.
646
- 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
647
- 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).
648
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.
649
- 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.
650
- old: BeginChild("Name", size, true)
651
- new: BeginChild("Name", size, ImGuiChildFlags_Border)
652
- old: BeginChild("Name", size, false)
653
- new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
654
**AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
655
- 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.
656
- old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
657
- new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
658
- 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).
659
- 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)
660
- 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).
661
- old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
662
- new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
663
- old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
664
- new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
665
- 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
666
- GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
667
- ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
668
- 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
669
- 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
670
- 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)
671
- 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).
672
- 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
673
- 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.
674
- 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)
675
- 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.
676
- 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.
677
- 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
678
- ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
679
- ListBoxFooter() -> use EndListBox()
680
- 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().
681
- 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
682
- 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
683
- ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp
684
- ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
685
- ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic()
686
- ImDrawList::PathBezierCurveTo() -> use ImDrawList::PathBezierCubicCurveTo()
687
- 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
688
- 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.
689
- 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
690
Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
691
it has been frequently requested by people to use our own. We had an opt-in define which was
692
previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
693
- OK: #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
694
- Error: #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
695
- 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.
696
- 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
697
- 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.
698
- 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
699
- ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl
700
- ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
701
- ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt
702
- ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
703
the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
704
the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
705
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.
706
- 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
707
this will require uses of legacy backend-dependent indices to be casted, e.g.
708
- with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
709
- with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A')
710
- etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
711
- 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);
712
- 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
713
- 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.
714
- 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.
715
- BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
716
- 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
717
this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
718
- previously this would make the window content size ~200x200:
719
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
720
- instead, please submit an item:
721
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
722
- alternative:
723
Begin(...) + Dummy(ImVec2(200,200)) + End();
724
- content size is now only extended when submitting an item!
725
- with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
726
- without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
727
- 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
728
- added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
729
- 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));
730
- 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.
731
- had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
732
- 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));
733
- requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
734
- always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
735
- 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
736
- Official backends from 1.87+ -> no issue.
737
- Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
738
- Custom backends not writing to io.NavInputs[] -> no issue.
739
- Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
740
- TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
741
- 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
742
- 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.
743
- 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.
744
- 2022/01/20 (1.87) - inputs: reworded gamepad IO.
745
- Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
746
- 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputting text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).
747
- 2022/01/17 (1.87) - inputs: reworked mouse IO.
748
- Backend writing to io.MousePos -> backend should call io.AddMousePosEvent()
749
- Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent()
750
- Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent()
751
- Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
752
note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
753
read https://github.com/ocornut/imgui/issues/4921 for details.
754
- 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(), ImGui::IsKeyDown(). 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.
755
- IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
756
- IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
757
- Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to still function with legacy key codes).
758
- 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.*
759
- 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.
760
- 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.
761
- 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
762
- 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'.
763
- 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)
764
- ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen()
765
- ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
766
- ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
767
- ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect
768
- ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
769
- 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
770
- 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.
771
- 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.
772
- 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
773
- ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
774
- ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
775
- 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
776
- if you are using official backends from the source tree: you have nothing to do.
777
- if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
778
- 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
779
- ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
780
- ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
781
- ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
782
flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
783
breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
784
- rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
785
- rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
786
- rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
787
- rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
788
this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
789
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.
790
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).
791
- 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
792
- ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
793
- 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.
794
- 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.
795
- 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'.
796
- 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.
797
- 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
798
- removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
799
- renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
800
- 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.
801
- renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
802
- renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
803
- 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
804
- ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
805
- ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
806
- ImGuiInputTextCallback -> use ImGuiTextEditCallback
807
- ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
808
- 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
809
- 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
810
- 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
811
- 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
812
- 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
813
- 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
814
- io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
815
- ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
816
- ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
817
- ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
818
- ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
819
- removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
820
- InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
821
- same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
822
- 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).
823
- 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
824
- 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
825
- 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.
826
- 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.
827
- 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!
828
- 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().
829
replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
830
worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
831
- if you omitted the 'power' parameter (likely!), you are not affected.
832
- 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.
833
- 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.
834
see https://github.com/ocornut/imgui/issues/3361 for all details.
835
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.
836
for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
837
- 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.
838
- 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.
839
- 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
840
- 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
841
- 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
842
- 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
843
- 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.
844
- 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.
845
- 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
846
- ShowTestWindow() -> use ShowDemoWindow()
847
- IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
848
- IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
849
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f))
850
- GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
851
- ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
852
- ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
853
- ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
854
- IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
855
- 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.
856
- 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).
857
- 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.
858
- 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
859
- 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
860
- 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
861
- Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
862
- IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
863
- AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
864
- SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
865
- ImFont::Glyph -> use ImFontGlyph
866
- 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.
867
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.
868
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).
869
If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
870
- 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
871
- 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
872
- 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.
873
- 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
874
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.
875
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.
876
Please reach out if you are affected.
877
- 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
878
- 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
879
- 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.
880
- 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
881
- 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
882
- 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
883
- 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!
884
- 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
885
- 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
886
- 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
887
- 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.
888
- 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
889
- 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
890
- 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
891
- 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.
892
If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
893
- 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
894
- 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.
895
NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
896
Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
897
- 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).
898
- 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
899
- 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
900
- 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.
901
- 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
902
- 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
903
- 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
904
- 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.).
905
old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
906
when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
907
in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
908
- 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
909
- 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
910
- 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.
911
If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
912
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.
913
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.
914
- 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",
915
consistent with other functions. Kept redirection functions (will obsolete).
916
- 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.
917
- 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).
918
- 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
919
- 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.
920
- 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
921
- 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.
922
- 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.
923
- 2018/02/07 (1.60) - reorganized context handling to be more explicit,
924
- YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
925
- removed Shutdown() function, as DestroyContext() serve this purpose.
926
- you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
927
- removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
928
- removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
929
- 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.
930
- 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
931
- 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
932
- 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
933
- 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.
934
- 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
935
- 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
936
- 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.
937
- 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.
938
- 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
939
- 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
940
- obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
941
- 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
942
- 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
943
- 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.
944
- 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.
945
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.
946
- 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
947
- 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
948
- 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
949
- 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
950
- 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.
951
- 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.
952
- 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.
953
removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
954
IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
955
IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
956
IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
957
- 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
958
- 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
959
- 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
960
- 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).
961
- 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)".
962
- 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
963
- renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
964
- renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
965
- 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
966
- 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 explicitly to fix.
967
- 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
968
- 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.
969
- 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).
970
- 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).
971
- 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
972
- 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.
973
- 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.
974
- 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))'
975
- 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
976
- 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
977
- 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
978
- 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().
979
- 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
980
- 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.
981
- 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.
982
- 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
983
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.
984
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:
985
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); }
986
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.
987
- 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
988
- 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
989
- 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).
990
- 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.
991
- 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).
992
- 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)
993
- 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
994
- 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.
995
- 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.
996
- 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
997
- 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
998
- 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
999
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.
1000
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!
1001
- 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
1002
- 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.
1003
- 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
1004
- 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.
1005
you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
1006
- 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.
1007
this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
1008
- 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.
1009
- the signature of the io.RenderDrawListsFn handler has changed!
1010
old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
1011
new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
1012
parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
1013
ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
1014
ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
1015
- 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.
1016
- 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!
1017
- refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
1018
- 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
1019
- 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
1020
- 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.
1021
- 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
1022
- 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!
1023
- 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
1024
- 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
1025
- 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.
1026
- 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.
1027
- 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
1028
- 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
1029
- 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
1030
- 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
1031
- 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
1032
- 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
1033
- 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
1034
- 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
1035
- 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
1036
- 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
1037
- 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
1038
- 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
1039
- 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
1040
- 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
1041
- 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
1042
- 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
1043
- 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
1044
- 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
1045
- old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
1046
- new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
1047
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.
1048
- 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()
1049
- 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
1050
- 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
1051
- 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
1052
- 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
1053
- 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
1054
- 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
1055
- 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)
1056
- 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
1057
- 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
1058
- 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
1059
- 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
1060
- 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
1061
- 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
1062
1063
1064
FREQUENTLY ASKED QUESTIONS (FAQ)
1065
================================
1066
1067
Read all answers online:
1068
https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
1069
Read all answers locally (with a text editor or ideally a Markdown viewer):
1070
docs/FAQ.md
1071
Some answers are copied down here to facilitate searching in code.
1072
1073
Q&A: Basics
1074
===========
1075
1076
Q: Where is the documentation?
1077
A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
1078
- Run the examples/ applications and explore them.
1079
- Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
1080
- See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
1081
- The demo covers most features of Dear ImGui, so you can read the code and see its output.
1082
- See documentation and comments at the top of imgui.cpp + effectively imgui.h.
1083
- 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
1084
examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
1085
- The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
1086
- The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
1087
- Your programming IDE is your friend, find the type or function declaration to find comments
1088
associated with it.
1089
1090
Q: What is this library called?
1091
Q: What is the difference between Dear ImGui and traditional UI toolkits?
1092
Q: Which version should I get?
1093
>> This library is called "Dear ImGui", please don't call it "ImGui" :)
1094
>> See https://www.dearimgui.com/faq for details.
1095
1096
Q&A: Integration
1097
================
1098
1099
Q: How to get started?
1100
A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
1101
1102
Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
1103
A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
1104
>> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
1105
1106
Q. How can I enable keyboard or gamepad controls?
1107
Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
1108
Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
1109
Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
1110
Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
1111
>> See https://www.dearimgui.com/faq
1112
1113
Q&A: Usage
1114
----------
1115
1116
Q: About the ID Stack system..
1117
- Why is my widget not reacting when I click on it?
1118
- How can I have widgets with an empty label?
1119
- How can I have multiple widgets with the same label?
1120
- How can I have multiple windows with the same label?
1121
Q: How can I display an image? What is ImTextureID, how does it work?
1122
Q: How can I use my own math types instead of ImVec2?
1123
Q: How can I interact with standard C++ types (such as std::string and std::vector)?
1124
Q: How can I display custom shapes? (using low-level ImDrawList API)
1125
>> See https://www.dearimgui.com/faq
1126
1127
Q&A: Fonts, Text
1128
================
1129
1130
Q: How should I handle DPI in my application?
1131
Q: How can I load a different font than the default?
1132
Q: How can I easily use icons in my application?
1133
Q: How can I load multiple fonts?
1134
Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
1135
>> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
1136
1137
Q&A: Concerns
1138
=============
1139
1140
Q: Who uses Dear ImGui?
1141
Q: Can you create elaborate/serious tools with Dear ImGui?
1142
Q: Can you reskin the look of Dear ImGui?
1143
Q: Why using C++ (as opposed to C)?
1144
>> See https://www.dearimgui.com/faq
1145
1146
Q&A: Community
1147
==============
1148
1149
Q: How can I help?
1150
A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
1151
We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
1152
This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
1153
>>> See https://github.com/ocornut/imgui/wiki/Funding
1154
- Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1155
- 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!
1156
- Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
1157
You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
1158
But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
1159
- 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).
1160
1161
*/
1162
1163
//-------------------------------------------------------------------------
1164
// [SECTION] INCLUDES
1165
//-------------------------------------------------------------------------
1166
1167
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
1168
#define _CRT_SECURE_NO_WARNINGS
1169
#endif
1170
1171
#ifndef IMGUI_DEFINE_MATH_OPERATORS
1172
#define IMGUI_DEFINE_MATH_OPERATORS
1173
#endif
1174
1175
#include "imgui.h"
1176
#ifndef IMGUI_DISABLE
1177
#include "imgui_internal.h"
1178
1179
// System includes
1180
#include <stdio.h> // vsnprintf, sscanf, printf
1181
#include <stdint.h> // intptr_t
1182
1183
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1184
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1185
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1186
#endif
1187
1188
// [Windows] OS specific includes (optional)
1189
#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1190
#define IMGUI_DISABLE_WIN32_FUNCTIONS
1191
#endif
1192
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1193
#ifndef WIN32_LEAN_AND_MEAN
1194
#define WIN32_LEAN_AND_MEAN
1195
#endif
1196
#ifndef NOMINMAX
1197
#define NOMINMAX
1198
#endif
1199
#ifndef __MINGW32__
1200
#include <Windows.h> // _wfopen, OpenClipboard
1201
#else
1202
#include <windows.h>
1203
#endif
1204
#if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
1205
// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1206
#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1207
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1208
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1209
#endif
1210
#endif
1211
1212
// [Apple] OS specific includes
1213
#if defined(__APPLE__)
1214
#include <TargetConditionals.h>
1215
#endif
1216
1217
// Visual Studio warnings
1218
#ifdef _MSC_VER
1219
#pragma warning (disable: 4127) // condition expression is constant
1220
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1221
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
1222
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
1223
#endif
1224
#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).
1225
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1226
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1227
#endif
1228
1229
// Clang/GCC warnings with -Weverything
1230
#if defined(__clang__)
1231
#if __has_warning("-Wunknown-warning-option")
1232
#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!
1233
#endif
1234
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
1235
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
1236
#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.
1237
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
1238
#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.
1239
#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.
1240
#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.
1241
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
1242
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
1243
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
1244
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
1245
#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.
1246
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
1247
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
1248
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
1249
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
1250
#elif defined(__GNUC__)
1251
// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1252
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
1253
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
1254
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
1255
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
1256
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
1257
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
1258
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1259
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
1260
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1261
#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
1262
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
1263
#endif
1264
1265
// Debug options
1266
#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Hold Ctrl to display for all candidates. Ctrl+Arrow to change last direction.
1267
#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
1268
1269
// Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls.
1270
static const float FONT_DEFAULT_SIZE_BASE = 20.0f;
1271
1272
// 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.
1273
static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
1274
static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
1275
static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut.
1276
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
1277
static 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.
1278
1279
// Tooltip offset
1280
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
1281
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20); // Multiplied by g.Style.MouseCursorScale
1282
static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f); // Multiplied by g.Style.MouseCursorScale
1283
1284
//-------------------------------------------------------------------------
1285
// [SECTION] FORWARD DECLARATIONS
1286
//-------------------------------------------------------------------------
1287
1288
static void SetCurrentWindow(ImGuiWindow* window);
1289
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1290
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1291
1292
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1293
1294
// Settings
1295
static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1296
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1297
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1298
static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1299
static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1300
1301
// Platform Dependents default implementation for ImGuiPlatformIO functions
1302
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);
1303
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);
1304
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1305
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
1306
1307
namespace ImGui
1308
{
1309
// Item
1310
static void ItemHandleShortcut(ImGuiID id);
1311
1312
// Window Focus
1313
static int FindWindowFocusIndex(ImGuiWindow* window);
1314
static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags);
1315
1316
// Navigation
1317
static void NavUpdate();
1318
static void NavUpdateWindowing();
1319
static void NavUpdateWindowingApplyFocus(ImGuiWindow* window);
1320
static void NavUpdateWindowingOverlay();
1321
static void NavUpdateCancelRequest();
1322
static void NavUpdateCreateMoveRequest();
1323
static void NavUpdateCreateTabbingRequest();
1324
static float NavUpdatePageUpPageDown();
1325
static inline void NavUpdateAnyRequestFlag();
1326
static void NavUpdateCreateWrappingRequest();
1327
static void NavEndFrame();
1328
static bool NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb);
1329
static void NavApplyItemToResult(ImGuiNavItemData* result);
1330
static void NavProcessItem();
1331
static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1332
static ImGuiInputSource NavCalcPreferredRefPosSource();
1333
static ImVec2 NavCalcPreferredRefPos();
1334
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1335
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1336
static void NavRestoreLayer(ImGuiNavLayer layer);
1337
1338
// Error Checking and Debug Tools
1339
static void ErrorCheckNewFrameSanityChecks();
1340
static void ErrorCheckEndFrameSanityChecks();
1341
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1342
static void UpdateDebugToolItemPicker();
1343
static void UpdateDebugToolItemPathQuery();
1344
static void UpdateDebugToolFlashStyleColor();
1345
#endif
1346
1347
// Inputs
1348
static void UpdateKeyboardInputs();
1349
static void UpdateMouseInputs();
1350
static void UpdateMouseWheel();
1351
static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1352
1353
// Misc
1354
static void UpdateFontsNewFrame();
1355
static void UpdateFontsEndFrame();
1356
static void UpdateTexturesNewFrame();
1357
static void UpdateTexturesEndFrame();
1358
static void UpdateSettings();
1359
static int UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
1360
static void RenderWindowOuterBorders(ImGuiWindow* window);
1361
static 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);
1362
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1363
static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1364
static void RenderDimmedBackgrounds();
1365
static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1366
static void SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect);
1367
1368
// Viewports
1369
const 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.
1370
static void UpdateViewportsNewFrame();
1371
1372
}
1373
1374
//-----------------------------------------------------------------------------
1375
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1376
//-----------------------------------------------------------------------------
1377
1378
// DLL users:
1379
// - Heaps and globals are not shared across DLL boundaries!
1380
// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1381
// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1382
// - 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.
1383
// - 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).
1384
1385
// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1386
// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1387
// Change to a different context by calling ImGui::SetCurrentContext().
1388
// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1389
// If you want thread-safety to allow N threads to access N different contexts:
1390
// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1391
// struct ImGuiContext;
1392
// extern thread_local ImGuiContext* MyImGuiTLS;
1393
// #define GImGui MyImGuiTLS
1394
// 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.
1395
// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1396
// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1397
// - DLL users: read comments above.
1398
#ifndef GImGui
1399
ImGuiContext* GImGui = NULL;
1400
#endif
1401
1402
// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1403
// - 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.
1404
// - DLL users: read comments above.
1405
#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1406
static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
1407
static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
1408
#else
1409
static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1410
static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1411
#endif
1412
static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
1413
static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
1414
static void* GImAllocatorUserData = NULL;
1415
1416
//-----------------------------------------------------------------------------
1417
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1418
//-----------------------------------------------------------------------------
1419
1420
ImGuiStyle::ImGuiStyle()
1421
{
1422
FontSizeBase = 0.0f; // Will default to io.Fonts->Fonts[0] on first frame.
1423
FontScaleMain = 1.0f; // Main scale factor. May be set by application once, or exposed to end-user.
1424
FontScaleDpi = 1.0f; // Additional scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.
1425
1426
Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
1427
DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1428
WindowPadding = ImVec2(8,8); // Padding within a window
1429
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.
1430
WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1431
WindowBorderHoverPadding = 4.0f; // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders.
1432
WindowMinSize = ImVec2(32,32); // Minimum window size
1433
WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1434
WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1435
ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1436
ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1437
PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1438
PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1439
FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1440
FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1441
FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1442
ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1443
ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1444
CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1445
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!
1446
IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1447
ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1448
ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1449
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1450
ScrollbarPadding = 2.0f; // Padding of scrollbar grab within its frame (same for both axises)
1451
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
1452
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1453
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1454
ImageBorderSize = 0.0f; // Thickness of border around tabs.
1455
TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1456
TabBorderSize = 0.0f; // Thickness of border around tabs.
1457
TabMinWidthBase = 1.0f; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
1458
TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.
1459
TabCloseButtonMinWidthSelected = -1.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
1460
TabCloseButtonMinWidthUnselected = 0.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
1461
TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1462
TabBarOverlineSize = 1.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar.
1463
TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1464
TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1465
TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone;
1466
TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
1467
TreeLinesRounding = 0.0f; // Radius of lines connecting child nodes to the vertical line.
1468
DragDropTargetRounding = 0.0f; // Radius of the drag and drop target frame.
1469
DragDropTargetBorderSize = 2.0f; // Thickness of the drag and drop target border.
1470
DragDropTargetPadding = 3.0f; // Size to expand the drag and drop target from actual target item size.
1471
ColorMarkerSize = 3.0f; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers.
1472
ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1473
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1474
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.
1475
SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText()
1476
SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1477
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.
1478
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.
1479
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.
1480
MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1481
AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1482
AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1483
AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1484
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.
1485
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.
1486
1487
// Behaviors
1488
HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1489
HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1490
HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1491
HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1492
HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1493
ScrollStepSize = ImVec2(0.0f, 0.0f);// Disabled by default.
1494
ScrollSmooth = 1.0f; // Disabled by default. It's just immediate jump from ScrollExpected to the visual Scroll.
1495
1496
// [Internal]
1497
_MainScale = 1.0f;
1498
_NextFrameFontSizeBase = 0.0f;
1499
1500
// Default theme
1501
ImGui::StyleColorsDark(this);
1502
}
1503
1504
1505
// Scale all spacing/padding/thickness values. Do not scale fonts.
1506
// 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.
1507
void ImGuiStyle::ScaleAllSizes(float scale_factor)
1508
{
1509
_MainScale *= scale_factor;
1510
WindowPadding = ImVec2(ImCeil(WindowPadding.x * scale_factor), ImCeil(WindowPadding.x * scale_factor));
1511
WindowRounding = ImCeil(WindowRounding * scale_factor);
1512
WindowMinSize = ImVec2(ImCeil(WindowMinSize.x * scale_factor), ImCeil(WindowMinSize.y * scale_factor));
1513
WindowBorderHoverPadding = ImCeil(WindowBorderHoverPadding * scale_factor);
1514
ChildRounding = ImCeil(ChildRounding * scale_factor);
1515
PopupRounding = ImCeil(PopupRounding * scale_factor);
1516
FramePadding = ImVec2(ImCeil(FramePadding.x * scale_factor), ImCeil(FramePadding.y * scale_factor));
1517
FrameRounding = ImCeil(FrameRounding * scale_factor);
1518
ItemSpacing = ImVec2(ImCeil(ItemSpacing.x * scale_factor), ImCeil(ItemSpacing.y * scale_factor));
1519
ItemInnerSpacing = ImVec2(ImCeil(ItemInnerSpacing.x * scale_factor), ImCeil(ItemInnerSpacing.y * scale_factor));
1520
CellPadding = ImVec2(ImCeil(CellPadding.x * scale_factor), ImCeil(CellPadding.y * scale_factor));
1521
TouchExtraPadding = ImVec2(ImCeil(TouchExtraPadding.x * scale_factor), ImCeil(TouchExtraPadding.y * scale_factor));
1522
IndentSpacing = ImCeil(IndentSpacing * scale_factor);
1523
ColumnsMinSpacing = ImCeil(ColumnsMinSpacing * scale_factor);
1524
ScrollbarSize = ImCeil(ScrollbarSize * scale_factor);
1525
ScrollbarRounding = ImCeil(ScrollbarRounding * scale_factor);
1526
ScrollbarPadding = ImCeil(ScrollbarPadding * scale_factor);
1527
GrabMinSize = ImCeil(GrabMinSize * scale_factor);
1528
GrabRounding = ImCeil(GrabRounding * scale_factor);
1529
LogSliderDeadzone = ImCeil(LogSliderDeadzone * scale_factor);
1530
ImageBorderSize = ImCeil(ImageBorderSize * scale_factor);
1531
TabRounding = ImCeil(TabRounding * scale_factor);
1532
TabMinWidthBase = ImCeil(TabMinWidthBase * scale_factor);
1533
TabMinWidthShrink = ImCeil(TabMinWidthShrink * scale_factor);
1534
TabCloseButtonMinWidthSelected = (TabCloseButtonMinWidthSelected > 0.0f && TabCloseButtonMinWidthSelected != FLT_MAX) ? ImCeil(TabCloseButtonMinWidthSelected * scale_factor) : TabCloseButtonMinWidthSelected;
1535
TabCloseButtonMinWidthUnselected = (TabCloseButtonMinWidthUnselected > 0.0f && TabCloseButtonMinWidthUnselected != FLT_MAX) ? ImCeil(TabCloseButtonMinWidthUnselected * scale_factor) : TabCloseButtonMinWidthUnselected;
1536
TabBarOverlineSize = ImCeil(TabBarOverlineSize * scale_factor);
1537
TreeLinesRounding = ImCeil(TreeLinesRounding * scale_factor);
1538
DragDropTargetRounding = ImCeil(DragDropTargetRounding * scale_factor);
1539
DragDropTargetBorderSize = ImCeil(DragDropTargetBorderSize * scale_factor);
1540
DragDropTargetPadding = ImCeil(DragDropTargetPadding * scale_factor);
1541
ColorMarkerSize = ImCeil(ColorMarkerSize * scale_factor);
1542
SeparatorTextPadding = ImVec2(ImCeil(SeparatorTextPadding.x * scale_factor), ImCeil(SeparatorTextPadding.y * scale_factor));
1543
DisplayWindowPadding = ImVec2(ImCeil(DisplayWindowPadding.x * scale_factor), ImCeil(DisplayWindowPadding.y * scale_factor));
1544
DisplaySafeAreaPadding = ImVec2(ImCeil(DisplaySafeAreaPadding.x * scale_factor), ImCeil(DisplaySafeAreaPadding.y * scale_factor));
1545
MouseCursorScale = ImCeil(MouseCursorScale * scale_factor);
1546
}
1547
1548
ImGuiIO::ImGuiIO()
1549
{
1550
// Most fields are initialized with zero
1551
memset(this, 0, sizeof(*this));
1552
IM_STATIC_ASSERT(IM_COUNTOF(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_COUNTOF(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1553
1554
// Settings
1555
ConfigFlags = ImGuiConfigFlags_None;
1556
BackendFlags = ImGuiBackendFlags_None;
1557
DisplaySize = ImVec2(-1.0f, -1.0f);
1558
DeltaTime = 1.0f / 60.0f;
1559
IniSavingRate = 5.0f;
1560
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).
1561
LogFilename = "imgui_log.txt";
1562
UserData = NULL;
1563
1564
Fonts = NULL;
1565
FontDefault = NULL;
1566
FontAllowUserScaling = false;
1567
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1568
FontGlobalScale = 1.0f; // Use style.FontScaleMain instead!
1569
#endif
1570
DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1571
1572
// Keyboard/Gamepad Navigation options
1573
ConfigNavSwapGamepadButtons = false;
1574
ConfigNavMoveSetMousePos = false;
1575
ConfigNavCaptureKeyboard = true;
1576
ConfigNavEscapeClearFocusItem = true;
1577
ConfigNavEscapeClearFocusWindow = false;
1578
ConfigNavCursorVisibleAuto = true;
1579
ConfigNavCursorVisibleAlways = false;
1580
1581
// Miscellaneous options
1582
MouseDrawCursor = false;
1583
#ifdef __APPLE__
1584
ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1585
#else
1586
ConfigMacOSXBehaviors = false;
1587
#endif
1588
ConfigInputTrickleEventQueue = true;
1589
ConfigInputTextCursorBlink = true;
1590
ConfigInputTextEnterKeepActive = false;
1591
ConfigDragClickToInputText = false;
1592
ConfigWindowsResizeFromEdges = true;
1593
ConfigWindowsMoveFromTitleBarOnly = false;
1594
ConfigWindowsCopyContentsWithCtrlC = false;
1595
ConfigScrollbarScrollByPage = true;
1596
ConfigMemoryCompactTimer = 60.0f;
1597
ConfigDebugIsDebuggerPresent = false;
1598
ConfigDebugHighlightIdConflicts = true;
1599
ConfigDebugHighlightIdConflictsShowItemPicker = true;
1600
ConfigDebugBeginReturnValueOnce = false;
1601
ConfigDebugBeginReturnValueLoop = false;
1602
1603
ConfigErrorRecovery = true;
1604
ConfigErrorRecoveryEnableAssert = true;
1605
ConfigErrorRecoveryEnableDebugLog = true;
1606
ConfigErrorRecoveryEnableTooltip = true;
1607
1608
// Inputs Behaviors
1609
MouseDoubleClickTime = 0.30f;
1610
MouseDoubleClickMaxDist = 6.0f;
1611
MouseDragThreshold = 6.0f;
1612
KeyRepeatDelay = 0.275f;
1613
KeyRepeatRate = 0.050f;
1614
1615
// Platform Functions
1616
// Note: Initialize() will setup default clipboard/ime handlers.
1617
BackendPlatformName = BackendRendererName = NULL;
1618
BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1619
1620
// Input (NB: we already have memset zero the entire structure!)
1621
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1622
MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1623
MouseSource = ImGuiMouseSource_Mouse;
1624
for (int i = 0; i < IM_COUNTOF(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1625
for (int i = 0; i < IM_COUNTOF(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1626
AppAcceptingEvents = true;
1627
}
1628
1629
// Pass in translated ASCII characters for text input.
1630
// - with glfw you can get those from the callback set in glfwSetCharCallback()
1631
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1632
// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1633
void ImGuiIO::AddInputCharacter(unsigned int c)
1634
{
1635
IM_ASSERT(Ctx != NULL);
1636
ImGuiContext& g = *Ctx;
1637
if (c == 0 || !AppAcceptingEvents)
1638
return;
1639
1640
ImGuiInputEvent e;
1641
e.Type = ImGuiInputEventType_Text;
1642
e.Source = ImGuiInputSource_Keyboard;
1643
e.EventId = g.InputEventsNextEventId++;
1644
e.Text.Char = c;
1645
g.InputEventsQueue.push_back(e);
1646
}
1647
1648
// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1649
// we should save the high surrogate.
1650
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1651
{
1652
if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1653
return;
1654
1655
if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1656
{
1657
if (InputQueueSurrogate != 0)
1658
AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1659
InputQueueSurrogate = c;
1660
return;
1661
}
1662
1663
ImWchar cp = c;
1664
if (InputQueueSurrogate != 0)
1665
{
1666
if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1667
{
1668
AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1669
}
1670
else
1671
{
1672
#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1673
cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1674
#else
1675
cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1676
#endif
1677
}
1678
1679
InputQueueSurrogate = 0;
1680
}
1681
AddInputCharacter((unsigned)cp);
1682
}
1683
1684
void ImGuiIO::AddInputCharactersUTF8(const char* str)
1685
{
1686
if (!AppAcceptingEvents)
1687
return;
1688
const char* str_end = str + strlen(str);
1689
while (*str != 0)
1690
{
1691
unsigned int c = 0;
1692
str += ImTextCharFromUtf8(&c, str, str_end);
1693
AddInputCharacter(c);
1694
}
1695
}
1696
1697
// Clear all incoming events.
1698
void ImGuiIO::ClearEventsQueue()
1699
{
1700
IM_ASSERT(Ctx != NULL);
1701
ImGuiContext& g = *Ctx;
1702
g.InputEventsQueue.clear();
1703
}
1704
1705
// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1706
void ImGuiIO::ClearInputKeys()
1707
{
1708
ImGuiContext& g = *Ctx;
1709
for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
1710
{
1711
if (ImGui::IsMouseKey((ImGuiKey)key))
1712
continue;
1713
ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
1714
key_data->Down = false;
1715
key_data->DownDuration = -1.0f;
1716
key_data->DownDurationPrev = -1.0f;
1717
}
1718
KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1719
KeyMods = ImGuiMod_None;
1720
InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
1721
}
1722
1723
void ImGuiIO::ClearInputMouse()
1724
{
1725
for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
1726
{
1727
ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN];
1728
key_data->Down = false;
1729
key_data->DownDuration = -1.0f;
1730
key_data->DownDurationPrev = -1.0f;
1731
}
1732
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1733
for (int n = 0; n < IM_COUNTOF(MouseDown); n++)
1734
{
1735
MouseDown[n] = false;
1736
MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1737
}
1738
MouseWheel = MouseWheelH = 0.0f;
1739
}
1740
1741
static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1742
{
1743
ImGuiContext& g = *ctx;
1744
for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1745
{
1746
ImGuiInputEvent* e = &g.InputEventsQueue[n];
1747
if (e->Type != type)
1748
continue;
1749
if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1750
continue;
1751
if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1752
continue;
1753
return e;
1754
}
1755
return NULL;
1756
}
1757
1758
// Queue a new key down/up event.
1759
// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1760
// - bool down: Is the key down? use false to signify a key release.
1761
// - float analog_value: 0.0f..1.0f
1762
// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1763
// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1764
void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1765
{
1766
//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); }
1767
IM_ASSERT(Ctx != NULL);
1768
if (key == ImGuiKey_None || !AppAcceptingEvents)
1769
return;
1770
ImGuiContext& g = *Ctx;
1771
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.
1772
IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1773
1774
// MacOS: swap Cmd(Super) and Ctrl
1775
if (g.IO.ConfigMacOSXBehaviors)
1776
{
1777
if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; }
1778
else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; }
1779
else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1780
else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1781
else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
1782
else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1783
}
1784
1785
// Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1786
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
1787
const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
1788
const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
1789
const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
1790
if (latest_key_down == down && latest_key_analog == analog_value)
1791
return;
1792
1793
// Add event
1794
ImGuiInputEvent e;
1795
e.Type = ImGuiInputEventType_Key;
1796
e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
1797
e.EventId = g.InputEventsNextEventId++;
1798
e.Key.Key = key;
1799
e.Key.Down = down;
1800
e.Key.AnalogValue = analog_value;
1801
g.InputEventsQueue.push_back(e);
1802
}
1803
1804
void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1805
{
1806
if (!AppAcceptingEvents)
1807
return;
1808
AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
1809
}
1810
1811
// [Optional] Call after AddKeyEvent().
1812
// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
1813
// 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.
1814
void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
1815
{
1816
if (key == ImGuiKey_None)
1817
return;
1818
IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
1819
IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
1820
IM_UNUSED(key); // Yet unused
1821
IM_UNUSED(native_keycode); // Yet unused
1822
IM_UNUSED(native_scancode); // Yet unused
1823
IM_UNUSED(native_legacy_index); // Yet unused
1824
}
1825
1826
// 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.
1827
void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1828
{
1829
AppAcceptingEvents = accepting_events;
1830
}
1831
1832
// Queue a mouse move event
1833
void ImGuiIO::AddMousePosEvent(float x, float y)
1834
{
1835
IM_ASSERT(Ctx != NULL);
1836
ImGuiContext& g = *Ctx;
1837
if (!AppAcceptingEvents)
1838
return;
1839
1840
// Apply same flooring as UpdateMouseInputs()
1841
ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
1842
1843
// Filter duplicate
1844
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
1845
const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1846
if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1847
return;
1848
1849
ImGuiInputEvent e;
1850
e.Type = ImGuiInputEventType_MousePos;
1851
e.Source = ImGuiInputSource_Mouse;
1852
e.EventId = g.InputEventsNextEventId++;
1853
e.MousePos.PosX = pos.x;
1854
e.MousePos.PosY = pos.y;
1855
e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1856
g.InputEventsQueue.push_back(e);
1857
}
1858
1859
void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1860
{
1861
IM_ASSERT(Ctx != NULL);
1862
ImGuiContext& g = *Ctx;
1863
IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1864
if (!AppAcceptingEvents)
1865
return;
1866
1867
// On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1868
if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1869
{
1870
// Order of both statements matters: this event will still release mouse button 1
1871
mouse_button = 1;
1872
if (!down)
1873
MouseCtrlLeftAsRightClick = false;
1874
}
1875
1876
// Filter duplicate
1877
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
1878
const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1879
if (latest_button_down == down)
1880
return;
1881
1882
// On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1883
// - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1884
// - At this point we want from !down to down, so this is handling the initial press.
1885
if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1886
{
1887
const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
1888
if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1889
{
1890
IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1891
MouseCtrlLeftAsRightClick = true;
1892
AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1893
return;
1894
}
1895
}
1896
1897
ImGuiInputEvent e;
1898
e.Type = ImGuiInputEventType_MouseButton;
1899
e.Source = ImGuiInputSource_Mouse;
1900
e.EventId = g.InputEventsNextEventId++;
1901
e.MouseButton.Button = mouse_button;
1902
e.MouseButton.Down = down;
1903
e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1904
g.InputEventsQueue.push_back(e);
1905
}
1906
1907
// Queue a mouse wheel event (some mouse/API may only have a Y component)
1908
void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1909
{
1910
IM_ASSERT(Ctx != NULL);
1911
ImGuiContext& g = *Ctx;
1912
1913
// Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1914
if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1915
return;
1916
1917
ImGuiInputEvent e;
1918
e.Type = ImGuiInputEventType_MouseWheel;
1919
e.Source = ImGuiInputSource_Mouse;
1920
e.EventId = g.InputEventsNextEventId++;
1921
e.MouseWheel.WheelX = wheel_x;
1922
e.MouseWheel.WheelY = wheel_y;
1923
e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1924
g.InputEventsQueue.push_back(e);
1925
}
1926
1927
// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1928
// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1929
void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1930
{
1931
IM_ASSERT(Ctx != NULL);
1932
ImGuiContext& g = *Ctx;
1933
g.InputEventsNextMouseSource = source;
1934
}
1935
1936
void ImGuiIO::AddFocusEvent(bool focused)
1937
{
1938
IM_ASSERT(Ctx != NULL);
1939
ImGuiContext& g = *Ctx;
1940
1941
// Filter duplicate
1942
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
1943
const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1944
if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1945
return;
1946
1947
ImGuiInputEvent e;
1948
e.Type = ImGuiInputEventType_Focus;
1949
e.EventId = g.InputEventsNextEventId++;
1950
e.AppFocused.Focused = focused;
1951
g.InputEventsQueue.push_back(e);
1952
}
1953
1954
ImGuiPlatformIO::ImGuiPlatformIO()
1955
{
1956
// Most fields are initialized with zero
1957
memset(this, 0, sizeof(*this));
1958
Platform_LocaleDecimalPoint = '.';
1959
}
1960
1961
//-----------------------------------------------------------------------------
1962
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1963
//-----------------------------------------------------------------------------
1964
1965
ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1966
{
1967
IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1968
ImVec2 p_last = p1;
1969
ImVec2 p_closest;
1970
float p_closest_dist2 = FLT_MAX;
1971
float t_step = 1.0f / (float)num_segments;
1972
for (int i_step = 1; i_step <= num_segments; i_step++)
1973
{
1974
ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1975
ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1976
float dist2 = ImLengthSqr(p - p_line);
1977
if (dist2 < p_closest_dist2)
1978
{
1979
p_closest = p_line;
1980
p_closest_dist2 = dist2;
1981
}
1982
p_last = p_current;
1983
}
1984
return p_closest;
1985
}
1986
1987
// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1988
static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1989
{
1990
float dx = x4 - x1;
1991
float dy = y4 - y1;
1992
float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1993
float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1994
d2 = (d2 >= 0) ? d2 : -d2;
1995
d3 = (d3 >= 0) ? d3 : -d3;
1996
if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1997
{
1998
ImVec2 p_current(x4, y4);
1999
ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
2000
float dist2 = ImLengthSqr(p - p_line);
2001
if (dist2 < p_closest_dist2)
2002
{
2003
p_closest = p_line;
2004
p_closest_dist2 = dist2;
2005
}
2006
p_last = p_current;
2007
}
2008
else if (level < 10)
2009
{
2010
float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
2011
float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
2012
float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
2013
float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
2014
float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
2015
float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
2016
ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
2017
ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
2018
}
2019
}
2020
2021
// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
2022
// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
2023
ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
2024
{
2025
IM_ASSERT(tess_tol > 0.0f);
2026
ImVec2 p_last = p1;
2027
ImVec2 p_closest;
2028
float p_closest_dist2 = FLT_MAX;
2029
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);
2030
return p_closest;
2031
}
2032
2033
ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
2034
{
2035
ImVec2 ap = p - a;
2036
ImVec2 ab_dir = b - a;
2037
float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
2038
if (dot < 0.0f)
2039
return a;
2040
float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
2041
if (dot > ab_len_sqr)
2042
return b;
2043
return a + ab_dir * dot / ab_len_sqr;
2044
}
2045
2046
bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
2047
{
2048
bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
2049
bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
2050
bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
2051
return (b1 == b2) && (b2 == b3);
2052
}
2053
2054
void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
2055
{
2056
ImVec2 v0 = b - a;
2057
ImVec2 v1 = c - a;
2058
ImVec2 v2 = p - a;
2059
const float denom = v0.x * v1.y - v1.x * v0.y;
2060
out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
2061
out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
2062
out_u = 1.0f - out_v - out_w;
2063
}
2064
2065
ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
2066
{
2067
ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
2068
ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
2069
ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
2070
float dist2_ab = ImLengthSqr(p - proj_ab);
2071
float dist2_bc = ImLengthSqr(p - proj_bc);
2072
float dist2_ca = ImLengthSqr(p - proj_ca);
2073
float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
2074
if (m == dist2_ab)
2075
return proj_ab;
2076
if (m == dist2_bc)
2077
return proj_bc;
2078
return proj_ca;
2079
}
2080
2081
//-----------------------------------------------------------------------------
2082
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
2083
//-----------------------------------------------------------------------------
2084
2085
// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
2086
int ImStricmp(const char* str1, const char* str2)
2087
{
2088
int d;
2089
while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
2090
return d;
2091
}
2092
2093
int ImStrnicmp(const char* str1, const char* str2, size_t count)
2094
{
2095
int d = 0;
2096
while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
2097
return d;
2098
}
2099
2100
void ImStrncpy(char* dst, const char* src, size_t count)
2101
{
2102
if (count < 1)
2103
return;
2104
if (count > 1)
2105
strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array
2106
dst[count - 1] = 0;
2107
}
2108
2109
char* ImStrdup(const char* str)
2110
{
2111
size_t len = ImStrlen(str);
2112
void* buf = IM_ALLOC(len + 1);
2113
return (char*)memcpy(buf, (const void*)str, len + 1);
2114
}
2115
2116
void* ImMemdup(const void* src, size_t size)
2117
{
2118
void* dst = IM_ALLOC(size);
2119
return memcpy(dst, src, size);
2120
}
2121
2122
char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
2123
{
2124
size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1;
2125
size_t src_size = ImStrlen(src) + 1;
2126
if (dst_buf_size < src_size)
2127
{
2128
IM_FREE(dst);
2129
dst = (char*)IM_ALLOC(src_size);
2130
if (p_dst_size)
2131
*p_dst_size = src_size;
2132
}
2133
return (char*)memcpy(dst, (const void*)src, src_size);
2134
}
2135
2136
const char* ImStrchrRange(const char* str, const char* str_end, char c)
2137
{
2138
const char* p = (const char*)ImMemchr(str, (int)c, str_end - str);
2139
return p;
2140
}
2141
2142
int ImStrlenW(const ImWchar* str)
2143
{
2144
//return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
2145
int n = 0;
2146
while (*str++) n++;
2147
return n;
2148
}
2149
2150
// Find end-of-line. Return pointer will point to either first \n, either str_end.
2151
const char* ImStreolRange(const char* str, const char* str_end)
2152
{
2153
const char* p = (const char*)ImMemchr(str, '\n', str_end - str);
2154
return p ? p : str_end;
2155
}
2156
2157
const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
2158
{
2159
IM_ASSERT_PARANOID(buf_mid_line >= buf_begin && buf_mid_line <= buf_begin + ImStrlen(buf_begin));
2160
while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
2161
buf_mid_line--;
2162
return buf_mid_line;
2163
}
2164
2165
const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
2166
{
2167
if (!needle_end)
2168
needle_end = needle + ImStrlen(needle);
2169
2170
const char un0 = (char)ImToUpper(*needle);
2171
while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
2172
{
2173
if (ImToUpper(*haystack) == un0)
2174
{
2175
const char* b = needle + 1;
2176
for (const char* a = haystack + 1; b < needle_end; a++, b++)
2177
if (ImToUpper(*a) != ImToUpper(*b))
2178
break;
2179
if (b == needle_end)
2180
return haystack;
2181
}
2182
haystack++;
2183
}
2184
return NULL;
2185
}
2186
2187
// 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.
2188
void ImStrTrimBlanks(char* buf)
2189
{
2190
char* p = buf;
2191
while (p[0] == ' ' || p[0] == '\t') // Leading blanks
2192
p++;
2193
char* p_start = p;
2194
while (*p != 0) // Find end of string
2195
p++;
2196
while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
2197
p--;
2198
if (p_start != buf) // Copy memory if we had leading blanks
2199
memmove(buf, p_start, p - p_start);
2200
buf[p - p_start] = 0; // Zero terminate
2201
}
2202
2203
const char* ImStrSkipBlank(const char* str)
2204
{
2205
while (str[0] == ' ' || str[0] == '\t')
2206
str++;
2207
return str;
2208
}
2209
2210
// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
2211
// 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.
2212
// B) When buf==NULL vsnprintf() will return the output size.
2213
#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2214
2215
// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
2216
// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2217
// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
2218
// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
2219
#ifdef IMGUI_USE_STB_SPRINTF
2220
#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
2221
#define STB_SPRINTF_IMPLEMENTATION
2222
#define STB_SPRINTF_STATIC
2223
#endif
2224
#ifdef IMGUI_STB_SPRINTF_FILENAME
2225
#include IMGUI_STB_SPRINTF_FILENAME
2226
#else
2227
#include "stb_sprintf.h"
2228
#endif
2229
#endif // #ifdef IMGUI_USE_STB_SPRINTF
2230
2231
#if defined(_MSC_VER) && !defined(vsnprintf)
2232
#define vsnprintf _vsnprintf
2233
#endif
2234
2235
int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
2236
{
2237
va_list args;
2238
va_start(args, fmt);
2239
#ifdef IMGUI_USE_STB_SPRINTF
2240
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2241
#else
2242
int w = vsnprintf(buf, buf_size, fmt, args);
2243
#endif
2244
va_end(args);
2245
if (buf == NULL)
2246
return w;
2247
if (w == -1 || w >= (int)buf_size)
2248
w = (int)buf_size - 1;
2249
buf[w] = 0;
2250
return w;
2251
}
2252
2253
int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
2254
{
2255
#ifdef IMGUI_USE_STB_SPRINTF
2256
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2257
#else
2258
int w = vsnprintf(buf, buf_size, fmt, args);
2259
#endif
2260
if (buf == NULL)
2261
return w;
2262
if (w == -1 || w >= (int)buf_size)
2263
w = (int)buf_size - 1;
2264
buf[w] = 0;
2265
return w;
2266
}
2267
#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2268
2269
void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
2270
{
2271
va_list args;
2272
va_start(args, fmt);
2273
ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
2274
va_end(args);
2275
}
2276
2277
// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
2278
// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
2279
// ImGuiTempBufferToken token;
2280
// ImFormatStringToTempBuffer(token, ...);
2281
void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
2282
{
2283
ImGuiContext& g = *GImGui;
2284
if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
2285
{
2286
const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
2287
if (buf == NULL)
2288
buf = "(null)";
2289
*out_buf = buf;
2290
if (out_buf_end) { *out_buf_end = buf + ImStrlen(buf); }
2291
}
2292
else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
2293
{
2294
int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
2295
const char* buf = va_arg(args, const char*);
2296
if (buf == NULL)
2297
{
2298
buf = "(null)";
2299
buf_len = ImMin(buf_len, 6);
2300
}
2301
*out_buf = buf;
2302
*out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
2303
}
2304
else
2305
{
2306
int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
2307
*out_buf = g.TempBuffer.Data;
2308
if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
2309
}
2310
}
2311
2312
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2313
// CRC32 needs a 1KB lookup table (not cache friendly)
2314
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
2315
// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
2316
static const ImU32 GCrc32LookupTable[256] =
2317
{
2318
#ifdef IMGUI_USE_LEGACY_CRC32_ADLER
2319
// Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data.
2320
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
2321
0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
2322
0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
2323
0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
2324
0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
2325
0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
2326
0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
2327
0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
2328
0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
2329
0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
2330
0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
2331
0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
2332
0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
2333
0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
2334
0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
2335
0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
2336
#else
2337
// CRC32c table compatible with SSE 4.2 instructions
2338
0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,
2339
0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,
2340
0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,
2341
0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,
2342
0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,
2343
0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,
2344
0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,
2345
0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,
2346
0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,
2347
0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,
2348
0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,
2349
0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,
2350
0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,
2351
0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,
2352
0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
2353
0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
2354
#endif
2355
};
2356
#endif
2357
2358
// Known size hash
2359
// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
2360
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2361
ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
2362
{
2363
ImU32 crc = ~seed;
2364
const unsigned char* data = (const unsigned char*)data_p;
2365
const unsigned char *data_end = (const unsigned char*)data_p + data_size;
2366
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2367
const ImU32* crc32_lut = GCrc32LookupTable;
2368
while (data < data_end)
2369
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
2370
return ~crc;
2371
#else
2372
while (data + 4 <= data_end)
2373
{
2374
crc = _mm_crc32_u32(crc, *(ImU32*)data);
2375
data += 4;
2376
}
2377
while (data < data_end)
2378
crc = _mm_crc32_u8(crc, *data++);
2379
return ~crc;
2380
#endif
2381
}
2382
2383
// Zero-terminated string hash, with support for ### to reset back to seed value.
2384
// e.g. "label###id" outputs the same hash as "id" (and "label" is generally displayed by the UI functions)
2385
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2386
ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
2387
{
2388
seed = ~seed;
2389
ImU32 crc = seed;
2390
const unsigned char* data = (const unsigned char*)data_p;
2391
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2392
const ImU32* crc32_lut = GCrc32LookupTable;
2393
#endif
2394
if (data_size != 0)
2395
{
2396
while (data_size-- > 0)
2397
{
2398
unsigned char c = *data++;
2399
if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
2400
{
2401
crc = seed;
2402
data += 2;
2403
data_size -= 2;
2404
continue;
2405
}
2406
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2407
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2408
#else
2409
crc = _mm_crc32_u8(crc, c);
2410
#endif
2411
}
2412
}
2413
else
2414
{
2415
while (unsigned char c = *data++)
2416
{
2417
if (c == '#' && data[0] == '#' && data[1] == '#')
2418
{
2419
crc = seed;
2420
data += 2;
2421
continue;
2422
}
2423
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2424
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2425
#else
2426
crc = _mm_crc32_u8(crc, c);
2427
#endif
2428
}
2429
}
2430
return ~crc;
2431
}
2432
2433
// Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2434
// FIXME-OPT: This is not designed to be optimal. Use with care.
2435
const char* ImHashSkipUncontributingPrefix(const char* label)
2436
{
2437
const char* result = label;
2438
while (unsigned char c = *label++)
2439
if (c == '#' && label[0] == '#' && label[1] == '#')
2440
result = label + 2;
2441
return result;
2442
}
2443
2444
//-----------------------------------------------------------------------------
2445
// [SECTION] MISC HELPERS/UTILITIES (File functions)
2446
//-----------------------------------------------------------------------------
2447
2448
// Default file functions
2449
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2450
2451
ImFileHandle ImFileOpen(const char* filename, const char* mode)
2452
{
2453
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!defined(__CYGWIN__) && !defined(__GNUC__)))
2454
// We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
2455
// Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
2456
const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
2457
const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
2458
2459
// Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
2460
// We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
2461
wchar_t local_temp_stack[FILENAME_MAX];
2462
ImVector<wchar_t> local_temp_heap;
2463
if (filename_wsize + mode_wsize > IM_COUNTOF(local_temp_stack))
2464
local_temp_heap.resize(filename_wsize + mode_wsize);
2465
wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
2466
wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
2467
::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
2468
::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
2469
return ::_wfopen(filename_wbuf, mode_wbuf);
2470
#else
2471
return fopen(filename, mode);
2472
#endif
2473
}
2474
2475
// 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.
2476
bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
2477
ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
2478
ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
2479
ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
2480
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2481
2482
// Helper: Load file content into memory
2483
// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
2484
// This can't really be used with "rt" because fseek size won't match read size.
2485
void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
2486
{
2487
IM_ASSERT(filename && mode);
2488
if (out_file_size)
2489
*out_file_size = 0;
2490
2491
ImFileHandle f;
2492
if ((f = ImFileOpen(filename, mode)) == NULL)
2493
return NULL;
2494
2495
size_t file_size = (size_t)ImFileGetSize(f);
2496
if (file_size == (size_t)-1)
2497
{
2498
ImFileClose(f);
2499
return NULL;
2500
}
2501
2502
void* file_data = IM_ALLOC(file_size + padding_bytes);
2503
if (file_data == NULL)
2504
{
2505
ImFileClose(f);
2506
return NULL;
2507
}
2508
if (ImFileRead(file_data, 1, file_size, f) != file_size)
2509
{
2510
ImFileClose(f);
2511
IM_FREE(file_data);
2512
return NULL;
2513
}
2514
if (padding_bytes > 0)
2515
memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
2516
2517
ImFileClose(f);
2518
if (out_file_size)
2519
*out_file_size = file_size;
2520
2521
return file_data;
2522
}
2523
2524
//-----------------------------------------------------------------------------
2525
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
2526
//-----------------------------------------------------------------------------
2527
2528
IM_MSVC_RUNTIME_CHECKS_OFF
2529
2530
// Convert UTF-8 to 32-bit character, process single character input.
2531
// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
2532
// We handle UTF-8 decoding error by skipping forward.
2533
int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
2534
{
2535
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 };
2536
static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
2537
static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
2538
static const int shiftc[] = { 0, 18, 12, 6, 0 };
2539
static const int shifte[] = { 0, 6, 4, 2, 0 };
2540
int len = lengths[*(const unsigned char*)in_text >> 3];
2541
int wanted = len + (len ? 0 : 1);
2542
2543
// IMPORTANT: if in_text_end == NULL it assume we have enough space!
2544
if (in_text_end == NULL)
2545
in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
2546
2547
// Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
2548
// so it is fast even with excessive branching.
2549
unsigned char s[4];
2550
s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
2551
s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
2552
s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
2553
s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
2554
2555
// Assume a four-byte character and load four bytes. Unused bits are shifted out.
2556
*out_char = (uint32_t)(s[0] & masks[len]) << 18;
2557
*out_char |= (uint32_t)(s[1] & 0x3f) << 12;
2558
*out_char |= (uint32_t)(s[2] & 0x3f) << 6;
2559
*out_char |= (uint32_t)(s[3] & 0x3f) << 0;
2560
*out_char >>= shiftc[len];
2561
2562
// Accumulate the various error conditions.
2563
int e = 0;
2564
e = (*out_char < mins[len]) << 6; // non-canonical encoding
2565
e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
2566
e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range we can store in ImWchar (FIXME: May evolve)
2567
e |= (s[1] & 0xc0) >> 2;
2568
e |= (s[2] & 0xc0) >> 4;
2569
e |= (s[3] ) >> 6;
2570
e ^= 0x2a; // top two bits of each tail byte correct?
2571
e >>= shifte[len];
2572
2573
if (e)
2574
{
2575
// No bytes are consumed when *in_text == 0 || in_text == in_text_end.
2576
// One byte is consumed in case of invalid first byte of in_text.
2577
// All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
2578
// Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
2579
wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
2580
*out_char = IM_UNICODE_CODEPOINT_INVALID;
2581
}
2582
2583
return wanted;
2584
}
2585
2586
int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
2587
{
2588
ImWchar* buf_out = buf;
2589
ImWchar* buf_end = buf + buf_size;
2590
while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2591
{
2592
unsigned int c;
2593
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2594
*buf_out++ = (ImWchar)c;
2595
}
2596
*buf_out = 0;
2597
if (in_text_remaining)
2598
*in_text_remaining = in_text;
2599
return (int)(buf_out - buf);
2600
}
2601
2602
int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
2603
{
2604
int char_count = 0;
2605
while ((!in_text_end || in_text < in_text_end) && *in_text)
2606
{
2607
unsigned int c;
2608
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2609
char_count++;
2610
}
2611
return char_count;
2612
}
2613
2614
// Based on stb_to_utf8() from github.com/nothings/stb/
2615
static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
2616
{
2617
if (c < 0x80)
2618
{
2619
buf[0] = (char)c;
2620
return 1;
2621
}
2622
if (c < 0x800)
2623
{
2624
if (buf_size < 2) return 0;
2625
buf[0] = (char)(0xc0 + (c >> 6));
2626
buf[1] = (char)(0x80 + (c & 0x3f));
2627
return 2;
2628
}
2629
if (c < 0x10000)
2630
{
2631
if (buf_size < 3) return 0;
2632
buf[0] = (char)(0xe0 + (c >> 12));
2633
buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
2634
buf[2] = (char)(0x80 + ((c ) & 0x3f));
2635
return 3;
2636
}
2637
if (c <= 0x10FFFF)
2638
{
2639
if (buf_size < 4) return 0;
2640
buf[0] = (char)(0xf0 + (c >> 18));
2641
buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
2642
buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
2643
buf[3] = (char)(0x80 + ((c ) & 0x3f));
2644
return 4;
2645
}
2646
// Invalid code point, the max unicode is 0x10FFFF
2647
return 0;
2648
}
2649
2650
int ImTextCharToUtf8(char out_buf[5], unsigned int c)
2651
{
2652
int count = ImTextCharToUtf8_inline(out_buf, 5, c);
2653
out_buf[count] = 0;
2654
return count;
2655
}
2656
2657
// Not optimal but we very rarely use this function.
2658
int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
2659
{
2660
unsigned int unused = 0;
2661
return ImTextCharFromUtf8(&unused, in_text, in_text_end);
2662
}
2663
2664
static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
2665
{
2666
if (c < 0x80) return 1;
2667
if (c < 0x800) return 2;
2668
if (c < 0x10000) return 3;
2669
if (c <= 0x10FFFF) return 4;
2670
return 3;
2671
}
2672
2673
int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
2674
{
2675
char* buf_p = out_buf;
2676
const char* buf_end = out_buf + out_buf_size;
2677
while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2678
{
2679
unsigned int c = (unsigned int)(*in_text++);
2680
if (c < 0x80)
2681
*buf_p++ = (char)c;
2682
else
2683
buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
2684
}
2685
*buf_p = 0;
2686
return (int)(buf_p - out_buf);
2687
}
2688
2689
int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
2690
{
2691
int bytes_count = 0;
2692
while ((!in_text_end || in_text < in_text_end) && *in_text)
2693
{
2694
unsigned int c = (unsigned int)(*in_text++);
2695
if (c < 0x80)
2696
bytes_count++;
2697
else
2698
bytes_count += ImTextCountUtf8BytesFromChar(c);
2699
}
2700
return bytes_count;
2701
}
2702
2703
const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_p)
2704
{
2705
while (in_p > in_text_start)
2706
{
2707
in_p--;
2708
if ((*in_p & 0xC0) != 0x80)
2709
return in_p;
2710
}
2711
return in_text_start;
2712
}
2713
2714
const char* ImTextFindValidUtf8CodepointEnd(const char* in_text_start, const char* in_text_end, const char* in_p)
2715
{
2716
if (in_text_start == in_p)
2717
return in_text_start;
2718
const char* prev = ImTextFindPreviousUtf8Codepoint(in_text_start, in_p);
2719
unsigned int prev_c;
2720
int prev_c_len = ImTextCharFromUtf8(&prev_c, prev, in_text_end);
2721
if (prev_c != IM_UNICODE_CODEPOINT_INVALID && prev_c_len <= (int)(in_p - prev))
2722
return in_p;
2723
return prev;
2724
}
2725
2726
int ImTextCountLines(const char* in_text, const char* in_text_end)
2727
{
2728
if (in_text_end == NULL)
2729
in_text_end = in_text + ImStrlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
2730
int count = 0;
2731
while (in_text < in_text_end)
2732
{
2733
const char* line_end = (const char*)ImMemchr(in_text, '\n', in_text_end - in_text);
2734
in_text = line_end ? line_end + 1 : in_text_end;
2735
count++;
2736
}
2737
return count;
2738
}
2739
2740
IM_MSVC_RUNTIME_CHECKS_RESTORE
2741
2742
//-----------------------------------------------------------------------------
2743
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
2744
// Note: The Convert functions are early design which are not consistent with other API.
2745
//-----------------------------------------------------------------------------
2746
2747
IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
2748
{
2749
float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
2750
int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
2751
int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
2752
int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
2753
return IM_COL32(r, g, b, 0xFF);
2754
}
2755
2756
ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
2757
{
2758
float s = 1.0f / 255.0f;
2759
return ImVec4(
2760
((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
2761
((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
2762
((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
2763
((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
2764
}
2765
2766
ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
2767
{
2768
ImU32 out;
2769
out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
2770
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
2771
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
2772
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
2773
return out;
2774
}
2775
2776
// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
2777
// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
2778
void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
2779
{
2780
float K = 0.f;
2781
if (g < b)
2782
{
2783
ImSwap(g, b);
2784
K = -1.f;
2785
}
2786
if (r < g)
2787
{
2788
ImSwap(r, g);
2789
K = -2.f / 6.f - K;
2790
}
2791
2792
const float chroma = r - (g < b ? g : b);
2793
out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
2794
out_s = chroma / (r + 1e-20f);
2795
out_v = r;
2796
}
2797
2798
// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
2799
// also http://en.wikipedia.org/wiki/HSL_and_HSV
2800
void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
2801
{
2802
if (s == 0.0f)
2803
{
2804
// gray
2805
out_r = out_g = out_b = v;
2806
return;
2807
}
2808
2809
h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
2810
int i = (int)h;
2811
float f = h - (float)i;
2812
float p = v * (1.0f - s);
2813
float q = v * (1.0f - s * f);
2814
float t = v * (1.0f - s * (1.0f - f));
2815
2816
switch (i)
2817
{
2818
case 0: out_r = v; out_g = t; out_b = p; break;
2819
case 1: out_r = q; out_g = v; out_b = p; break;
2820
case 2: out_r = p; out_g = v; out_b = t; break;
2821
case 3: out_r = p; out_g = q; out_b = v; break;
2822
case 4: out_r = t; out_g = p; out_b = v; break;
2823
case 5: default: out_r = v; out_g = p; out_b = q; break;
2824
}
2825
}
2826
2827
//-----------------------------------------------------------------------------
2828
// [SECTION] ImGuiStorage
2829
// Helper: Key->value storage
2830
//-----------------------------------------------------------------------------
2831
2832
// std::lower_bound but without the bullshit
2833
ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
2834
{
2835
ImGuiStoragePair* in_p = in_begin;
2836
for (size_t count = (size_t)(in_end - in_p); count > 0; )
2837
{
2838
size_t count2 = count >> 1;
2839
ImGuiStoragePair* mid = in_p + count2;
2840
if (mid->key < key)
2841
{
2842
in_p = ++mid;
2843
count -= count2 + 1;
2844
}
2845
else
2846
{
2847
count = count2;
2848
}
2849
}
2850
return in_p;
2851
}
2852
2853
IM_MSVC_RUNTIME_CHECKS_OFF
2854
static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
2855
{
2856
// We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
2857
ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
2858
ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
2859
return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
2860
}
2861
2862
// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2863
void ImGuiStorage::BuildSortByKey()
2864
{
2865
ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);
2866
}
2867
2868
int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
2869
{
2870
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2871
if (it == Data.Data + Data.Size || it->key != key)
2872
return default_val;
2873
return it->val_i;
2874
}
2875
2876
bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
2877
{
2878
return GetInt(key, default_val ? 1 : 0) != 0;
2879
}
2880
2881
float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
2882
{
2883
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2884
if (it == Data.Data + Data.Size || it->key != key)
2885
return default_val;
2886
return it->val_f;
2887
}
2888
2889
void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
2890
{
2891
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2892
if (it == Data.Data + Data.Size || it->key != key)
2893
return NULL;
2894
return it->val_p;
2895
}
2896
2897
// 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.
2898
int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
2899
{
2900
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2901
if (it == Data.Data + Data.Size || it->key != key)
2902
it = Data.insert(it, ImGuiStoragePair(key, default_val));
2903
return &it->val_i;
2904
}
2905
2906
bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2907
{
2908
return (bool*)GetIntRef(key, default_val ? 1 : 0);
2909
}
2910
2911
float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2912
{
2913
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2914
if (it == Data.Data + Data.Size || it->key != key)
2915
it = Data.insert(it, ImGuiStoragePair(key, default_val));
2916
return &it->val_f;
2917
}
2918
2919
void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2920
{
2921
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2922
if (it == Data.Data + Data.Size || it->key != key)
2923
it = Data.insert(it, ImGuiStoragePair(key, default_val));
2924
return &it->val_p;
2925
}
2926
2927
// 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)
2928
void ImGuiStorage::SetInt(ImGuiID key, int val)
2929
{
2930
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2931
if (it == Data.Data + Data.Size || it->key != key)
2932
Data.insert(it, ImGuiStoragePair(key, val));
2933
else
2934
it->val_i = val;
2935
}
2936
2937
void ImGuiStorage::SetBool(ImGuiID key, bool val)
2938
{
2939
SetInt(key, val ? 1 : 0);
2940
}
2941
2942
void ImGuiStorage::SetFloat(ImGuiID key, float val)
2943
{
2944
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2945
if (it == Data.Data + Data.Size || it->key != key)
2946
Data.insert(it, ImGuiStoragePair(key, val));
2947
else
2948
it->val_f = val;
2949
}
2950
2951
void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2952
{
2953
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2954
if (it == Data.Data + Data.Size || it->key != key)
2955
Data.insert(it, ImGuiStoragePair(key, val));
2956
else
2957
it->val_p = val;
2958
}
2959
2960
void ImGuiStorage::SetAllInt(int v)
2961
{
2962
for (int i = 0; i < Data.Size; i++)
2963
Data[i].val_i = v;
2964
}
2965
IM_MSVC_RUNTIME_CHECKS_RESTORE
2966
2967
//-----------------------------------------------------------------------------
2968
// [SECTION] ImGuiTextFilter
2969
//-----------------------------------------------------------------------------
2970
2971
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2972
ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
2973
{
2974
InputBuf[0] = 0;
2975
CountGrep = 0;
2976
if (default_filter)
2977
{
2978
ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf));
2979
Build();
2980
}
2981
}
2982
2983
bool ImGuiTextFilter::Draw(const char* label, float width)
2984
{
2985
if (width != 0.0f)
2986
ImGui::SetNextItemWidth(width);
2987
bool value_changed = ImGui::InputText(label, InputBuf, IM_COUNTOF(InputBuf));
2988
if (value_changed)
2989
Build();
2990
return value_changed;
2991
}
2992
2993
void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2994
{
2995
out->resize(0);
2996
const char* wb = b;
2997
const char* we = wb;
2998
while (we < e)
2999
{
3000
if (*we == separator)
3001
{
3002
out->push_back(ImGuiTextRange(wb, we));
3003
wb = we + 1;
3004
}
3005
we++;
3006
}
3007
if (wb != we)
3008
out->push_back(ImGuiTextRange(wb, we));
3009
}
3010
3011
void ImGuiTextFilter::Build()
3012
{
3013
Filters.resize(0);
3014
ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf));
3015
input_range.split(',', &Filters);
3016
3017
CountGrep = 0;
3018
for (ImGuiTextRange& f : Filters)
3019
{
3020
while (f.b < f.e && ImCharIsBlankA(f.b[0]))
3021
f.b++;
3022
while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
3023
f.e--;
3024
if (f.empty())
3025
continue;
3026
if (f.b[0] != '-')
3027
CountGrep += 1;
3028
}
3029
}
3030
3031
bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
3032
{
3033
if (Filters.Size == 0)
3034
return true;
3035
3036
if (text == NULL)
3037
text = text_end = "";
3038
3039
for (const ImGuiTextRange& f : Filters)
3040
{
3041
if (f.b == f.e)
3042
continue;
3043
if (f.b[0] == '-')
3044
{
3045
// Subtract
3046
if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
3047
return false;
3048
}
3049
else
3050
{
3051
// Grep
3052
if (ImStristr(text, text_end, f.b, f.e) != NULL)
3053
return true;
3054
}
3055
}
3056
3057
// Implicit * grep
3058
if (CountGrep == 0)
3059
return true;
3060
3061
return false;
3062
}
3063
3064
//-----------------------------------------------------------------------------
3065
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
3066
//-----------------------------------------------------------------------------
3067
3068
// On some platform vsnprintf() takes va_list by reference and modifies it.
3069
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
3070
#ifndef va_copy
3071
#if defined(__GNUC__) || defined(__clang__)
3072
#define va_copy(dest, src) __builtin_va_copy(dest, src)
3073
#else
3074
#define va_copy(dest, src) (dest = src)
3075
#endif
3076
#endif
3077
3078
char ImGuiTextBuffer::EmptyString[1] = { 0 };
3079
3080
void ImGuiTextBuffer::append(const char* str, const char* str_end)
3081
{
3082
int len = str_end ? (int)(str_end - str) : (int)ImStrlen(str);
3083
3084
// Add zero-terminator the first time
3085
const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
3086
const int needed_sz = write_off + len;
3087
if (write_off + len >= Buf.Capacity)
3088
{
3089
int new_capacity = Buf.Capacity * 2;
3090
Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
3091
}
3092
3093
Buf.resize(needed_sz);
3094
memcpy(&Buf[write_off - 1], str, (size_t)len);
3095
Buf[write_off - 1 + len] = 0;
3096
}
3097
3098
void ImGuiTextBuffer::appendf(const char* fmt, ...)
3099
{
3100
va_list args;
3101
va_start(args, fmt);
3102
appendfv(fmt, args);
3103
va_end(args);
3104
}
3105
3106
// Helper: Text buffer for logging/accumulating text
3107
void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
3108
{
3109
va_list args_copy;
3110
va_copy(args_copy, args);
3111
3112
int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
3113
if (len <= 0)
3114
{
3115
va_end(args_copy);
3116
return;
3117
}
3118
3119
// Add zero-terminator the first time
3120
const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
3121
const int needed_sz = write_off + len;
3122
if (write_off + len >= Buf.Capacity)
3123
{
3124
int new_capacity = Buf.Capacity * 2;
3125
Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
3126
}
3127
3128
Buf.resize(needed_sz);
3129
ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
3130
va_end(args_copy);
3131
}
3132
3133
IM_MSVC_RUNTIME_CHECKS_OFF
3134
void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
3135
{
3136
IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
3137
if (old_size == new_size)
3138
return;
3139
if (EndOffset == 0 || base[EndOffset - 1] == '\n')
3140
Offsets.push_back(EndOffset);
3141
const char* base_end = base + new_size;
3142
for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\n', base_end - p)) != 0; )
3143
if (++p < base_end) // Don't push a trailing offset on last \n
3144
Offsets.push_back((int)(intptr_t)(p - base));
3145
EndOffset = ImMax(EndOffset, new_size);
3146
}
3147
IM_MSVC_RUNTIME_CHECKS_RESTORE
3148
3149
//-----------------------------------------------------------------------------
3150
// [SECTION] ImGuiListClipper
3151
//-----------------------------------------------------------------------------
3152
3153
// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
3154
// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
3155
static bool GetSkipItemForListClipping()
3156
{
3157
ImGuiContext& g = *GImGui;
3158
return g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems;
3159
}
3160
3161
static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
3162
{
3163
if (ranges.Size - offset <= 1)
3164
return;
3165
3166
// Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
3167
for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
3168
for (int i = offset; i < sort_end + offset; ++i)
3169
if (ranges[i].Min > ranges[i + 1].Min)
3170
ImSwap(ranges[i], ranges[i + 1]);
3171
3172
// Now fuse ranges together as much as possible.
3173
for (int i = 1 + offset; i < ranges.Size; i++)
3174
{
3175
IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
3176
if (ranges[i - 1].Max < ranges[i].Min)
3177
continue;
3178
ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
3179
ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
3180
ranges.erase(ranges.Data + i);
3181
i--;
3182
}
3183
}
3184
3185
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clipper, float pos_y, float line_height)
3186
{
3187
// Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
3188
// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
3189
// 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?
3190
ImGuiContext& g = *GImGui;
3191
ImGuiWindow* window = g.CurrentWindow;
3192
float off_y = pos_y - window->DC.CursorPos.y;
3193
window->DC.CursorPos.y = pos_y;
3194
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
3195
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.
3196
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.
3197
if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
3198
columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
3199
if (ImGuiTable* table = g.CurrentTable)
3200
{
3201
if (table->IsInsideRow)
3202
ImGui::TableEndRow(table);
3203
const int row_increase = (int)((off_y / line_height) + 0.5f);
3204
if (row_increase > 0 && (clipper->Flags & ImGuiListClipperFlags_NoSetTableRowCounters) == 0) // If your clipper item height is != from actual table row height, consider using ImGuiListClipperFlags_NoSetTableRowCounters. See #8886.
3205
{
3206
table->CurrentRow += row_increase;
3207
table->RowBgColorCounter += row_increase;
3208
}
3209
table->RowPosY2 = window->DC.CursorPos.y;
3210
}
3211
}
3212
3213
ImGuiListClipper::ImGuiListClipper()
3214
{
3215
memset(this, 0, sizeof(*this));
3216
}
3217
3218
ImGuiListClipper::~ImGuiListClipper()
3219
{
3220
End();
3221
}
3222
3223
void ImGuiListClipper::Begin(int items_count, float items_height)
3224
{
3225
if (Ctx == NULL)
3226
Ctx = ImGui::GetCurrentContext();
3227
3228
ImGuiContext& g = *Ctx;
3229
ImGuiWindow* window = g.CurrentWindow;
3230
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
3231
3232
if (ImGuiTable* table = g.CurrentTable)
3233
if (table->IsInsideRow)
3234
ImGui::TableEndRow(table);
3235
3236
StartPosY = window->DC.CursorPos.y;
3237
ItemsHeight = items_height;
3238
ItemsCount = items_count;
3239
DisplayStart = -1;
3240
DisplayEnd = 0;
3241
3242
// Acquire temporary buffer
3243
if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
3244
g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
3245
ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3246
data->Reset(this);
3247
data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
3248
TempData = data;
3249
StartSeekOffsetY = data->LossynessOffset;
3250
}
3251
3252
void ImGuiListClipper::End()
3253
{
3254
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
3255
{
3256
// 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.
3257
ImGuiContext& g = *Ctx;
3258
IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
3259
if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
3260
SeekCursorForItem(ItemsCount);
3261
3262
// Restore temporary buffer and fix back pointers which may be invalidated when nesting
3263
IM_ASSERT(data->ListClipper == this);
3264
data->StepNo = data->Ranges.Size;
3265
if (--g.ClipperTempDataStacked > 0)
3266
{
3267
data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3268
data->ListClipper->TempData = data;
3269
}
3270
TempData = NULL;
3271
}
3272
ItemsCount = -1;
3273
}
3274
3275
void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
3276
{
3277
ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
3278
IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
3279
IM_ASSERT(item_begin <= item_end);
3280
if (item_begin < item_end)
3281
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
3282
}
3283
3284
// This is already called while stepping.
3285
// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.
3286
void ImGuiListClipper::SeekCursorForItem(int item_n)
3287
{
3288
// - Perform the add and multiply with double to allow seeking through larger ranges.
3289
// - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
3290
// - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
3291
float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
3292
ImGuiListClipper_SeekCursorAndSetupPrevLine(this, pos_y, ItemsHeight);
3293
}
3294
3295
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
3296
{
3297
ImGuiContext& g = *clipper->Ctx;
3298
ImGuiWindow* window = g.CurrentWindow;
3299
ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
3300
IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
3301
3302
ImGuiTable* table = g.CurrentTable;
3303
if (table && table->IsInsideRow)
3304
ImGui::TableEndRow(table);
3305
3306
// No items
3307
if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
3308
return false;
3309
3310
// While we are in frozen row state, keep displaying items one by one, unclipped
3311
// FIXME: Could be stored as a table-agnostic state.
3312
if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
3313
{
3314
clipper->DisplayStart = data->ItemsFrozen;
3315
clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
3316
if (clipper->DisplayStart < clipper->DisplayEnd)
3317
data->ItemsFrozen++;
3318
return true;
3319
}
3320
3321
// Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
3322
bool calc_clipping = false;
3323
if (data->StepNo == 0)
3324
{
3325
clipper->StartPosY = window->DC.CursorPos.y;
3326
if (clipper->ItemsHeight <= 0.0f)
3327
{
3328
// Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
3329
data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
3330
clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
3331
clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
3332
data->StepNo = 1;
3333
return true;
3334
}
3335
calc_clipping = true; // If on the first step with known item height, calculate clipping.
3336
}
3337
3338
// Step 1: Let the clipper infer height from first range
3339
if (clipper->ItemsHeight <= 0.0f)
3340
{
3341
IM_ASSERT(data->StepNo == 1);
3342
if (table)
3343
IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
3344
3345
bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
3346
if (affected_by_floating_point_precision)
3347
{
3348
// Mitigation/hack for very large range: assume last time height constitute line height.
3349
clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
3350
window->DC.CursorPos.y = (float)(clipper->StartPosY + clipper->ItemsHeight);
3351
}
3352
else
3353
{
3354
clipper->ItemsHeight = (float)(window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
3355
}
3356
if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
3357
return false;
3358
IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
3359
calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
3360
}
3361
3362
// Step 0 or 1: Calculate the actual ranges of visible elements.
3363
const int already_submitted = clipper->DisplayEnd;
3364
if (calc_clipping)
3365
{
3366
// Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
3367
clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
3368
3369
if (g.LogEnabled)
3370
{
3371
// If logging is active, do not perform any clipping
3372
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
3373
}
3374
else
3375
{
3376
// Add range selected to be included for navigation
3377
const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
3378
const int nav_off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
3379
const int nav_off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
3380
if (is_nav_request)
3381
{
3382
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, nav_off_min, nav_off_max));
3383
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, nav_off_min, nav_off_max));
3384
}
3385
if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
3386
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
3387
3388
// Add focused/active item
3389
ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
3390
if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
3391
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
3392
3393
float min_y = window->ClipRect.Min.y;
3394
float max_y = window->ClipRect.Max.y;
3395
3396
// Add box selection range
3397
ImGuiBoxSelectState* bs = &g.BoxSelectState;
3398
if (bs->IsActive && bs->Window == window)
3399
{
3400
// FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
3401
// RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
3402
// As a workaround we currently half ItemSpacing worth on each side.
3403
min_y -= g.Style.ItemSpacing.y;
3404
max_y += g.Style.ItemSpacing.y;
3405
3406
// Box-select on 2D area requires different clipping.
3407
if (bs->UnclipMode)
3408
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
3409
}
3410
3411
// Add main visible range
3412
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, nav_off_min, nav_off_max));
3413
}
3414
3415
// Convert position ranges to item index ranges
3416
// - 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.
3417
// - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
3418
// which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
3419
for (ImGuiListClipperRange& range : data->Ranges)
3420
if (range.PosToIndexConvert)
3421
{
3422
int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
3423
int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
3424
range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
3425
range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
3426
range.PosToIndexConvert = false;
3427
}
3428
ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
3429
}
3430
3431
// Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
3432
while (data->StepNo < data->Ranges.Size)
3433
{
3434
clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
3435
clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
3436
data->StepNo++;
3437
if (clipper->DisplayStart >= clipper->DisplayEnd)
3438
continue;
3439
if (clipper->DisplayStart > already_submitted)
3440
clipper->SeekCursorForItem(clipper->DisplayStart);
3441
return true;
3442
}
3443
3444
// After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
3445
// Advance the cursor to the end of the list and then returns 'false' to end the loop.
3446
if (clipper->ItemsCount < INT_MAX)
3447
clipper->SeekCursorForItem(clipper->ItemsCount);
3448
3449
return false;
3450
}
3451
3452
bool ImGuiListClipper::Step()
3453
{
3454
ImGuiContext& g = *Ctx;
3455
bool need_items_height = (ItemsHeight <= 0.0f);
3456
bool ret = ImGuiListClipper_StepInternal(this);
3457
if (ret && (DisplayStart >= DisplayEnd))
3458
ret = false;
3459
if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
3460
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
3461
if (need_items_height && ItemsHeight > 0.0f)
3462
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
3463
if (ret)
3464
{
3465
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
3466
}
3467
else
3468
{
3469
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
3470
End();
3471
}
3472
return ret;
3473
}
3474
3475
// Generic helper, equivalent to old ImGui::CalcListClipping() but statelesss
3476
void ImGui::CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end)
3477
{
3478
*out_visible_start = ImMax((int)((clip_rect.Min.y - pos.y) / items_height), 0);
3479
*out_visible_end = ImMax((int)ImCeil((clip_rect.Max.y - pos.y) / items_height), *out_visible_start);
3480
}
3481
3482
//-----------------------------------------------------------------------------
3483
// [SECTION] STYLING
3484
//-----------------------------------------------------------------------------
3485
3486
ImGuiStyle& ImGui::GetStyle()
3487
{
3488
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3489
return GImGui->Style;
3490
}
3491
3492
ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
3493
{
3494
ImGuiStyle& style = GImGui->Style;
3495
ImVec4 c = style.Colors[idx];
3496
c.w *= style.Alpha * alpha_mul;
3497
return ColorConvertFloat4ToU32(c);
3498
}
3499
3500
ImU32 ImGui::GetColorU32(const ImVec4& col)
3501
{
3502
ImGuiStyle& style = GImGui->Style;
3503
ImVec4 c = col;
3504
c.w *= style.Alpha;
3505
return ColorConvertFloat4ToU32(c);
3506
}
3507
3508
const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
3509
{
3510
ImGuiStyle& style = GImGui->Style;
3511
return style.Colors[idx];
3512
}
3513
3514
ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
3515
{
3516
ImGuiStyle& style = GImGui->Style;
3517
alpha_mul *= style.Alpha;
3518
if (alpha_mul >= 1.0f)
3519
return col;
3520
ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
3521
a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
3522
return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
3523
}
3524
3525
// 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
3526
void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
3527
{
3528
ImGuiContext& g = *GImGui;
3529
ImGuiColorMod backup;
3530
backup.Col = idx;
3531
backup.BackupValue = g.Style.Colors[idx];
3532
g.ColorStack.push_back(backup);
3533
if (g.DebugFlashStyleColorIdx != idx)
3534
g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
3535
}
3536
3537
void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
3538
{
3539
ImGuiContext& g = *GImGui;
3540
ImGuiColorMod backup;
3541
backup.Col = idx;
3542
backup.BackupValue = g.Style.Colors[idx];
3543
g.ColorStack.push_back(backup);
3544
if (g.DebugFlashStyleColorIdx != idx)
3545
g.Style.Colors[idx] = col;
3546
}
3547
3548
void ImGui::PopStyleColor(int count)
3549
{
3550
ImGuiContext& g = *GImGui;
3551
if (g.ColorStack.Size < count)
3552
{
3553
IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
3554
count = g.ColorStack.Size;
3555
}
3556
while (count > 0)
3557
{
3558
ImGuiColorMod& backup = g.ColorStack.back();
3559
g.Style.Colors[backup.Col] = backup.BackupValue;
3560
g.ColorStack.pop_back();
3561
count--;
3562
}
3563
}
3564
3565
static const ImGuiStyleVarInfo GStyleVarsInfo[] =
3566
{
3567
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
3568
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
3569
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
3570
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
3571
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
3572
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
3573
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
3574
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
3575
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
3576
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
3577
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
3578
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
3579
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
3580
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
3581
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
3582
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
3583
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
3584
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
3585
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
3586
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
3587
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) }, // ImGuiStyleVar_ScrollbarPadding
3588
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
3589
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3590
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize
3591
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
3592
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
3593
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthBase) }, // ImGuiStyleVar_TabMinWidthBase
3594
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthShrink) }, // ImGuiStyleVar_TabMinWidthShrink
3595
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
3596
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize
3597
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
3598
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3599
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize
3600
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding
3601
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
3602
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
3603
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
3604
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
3605
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
3606
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollStepSize) }, // ImGuiStyleVar_ScrollStepSize
3607
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollSmooth) }, // ImGuiStyleVar_ScrollSmooth
3608
};
3609
3610
const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
3611
{
3612
IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
3613
IM_STATIC_ASSERT(IM_COUNTOF(GStyleVarsInfo) == ImGuiStyleVar_COUNT);
3614
return &GStyleVarsInfo[idx];
3615
}
3616
3617
void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
3618
{
3619
ImGuiContext& g = *GImGui;
3620
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3621
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 1)
3622
{
3623
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3624
return;
3625
}
3626
float* pvar = (float*)var_info->GetVarPtr(&g.Style);
3627
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3628
*pvar = val;
3629
}
3630
3631
void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)
3632
{
3633
ImGuiContext& g = *GImGui;
3634
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3635
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3636
{
3637
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3638
return;
3639
}
3640
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3641
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3642
pvar->x = val_x;
3643
}
3644
3645
void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)
3646
{
3647
ImGuiContext& g = *GImGui;
3648
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3649
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3650
{
3651
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3652
return;
3653
}
3654
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3655
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3656
pvar->y = val_y;
3657
}
3658
3659
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
3660
{
3661
ImGuiContext& g = *GImGui;
3662
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3663
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3664
{
3665
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3666
return;
3667
}
3668
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3669
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3670
*pvar = val;
3671
}
3672
3673
void ImGui::PopStyleVar(int count)
3674
{
3675
ImGuiContext& g = *GImGui;
3676
if (g.StyleVarStack.Size < count)
3677
{
3678
IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
3679
count = g.StyleVarStack.Size;
3680
}
3681
while (count > 0)
3682
{
3683
// We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
3684
ImGuiStyleMod& backup = g.StyleVarStack.back();
3685
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(backup.VarIdx);
3686
void* data = var_info->GetVarPtr(&g.Style);
3687
if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
3688
else if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
3689
g.StyleVarStack.pop_back();
3690
count--;
3691
}
3692
}
3693
3694
const char* ImGui::GetStyleColorName(ImGuiCol idx)
3695
{
3696
// Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
3697
switch (idx)
3698
{
3699
case ImGuiCol_Text: return "Text";
3700
case ImGuiCol_TextDisabled: return "TextDisabled";
3701
case ImGuiCol_WindowBg: return "WindowBg";
3702
case ImGuiCol_ChildBg: return "ChildBg";
3703
case ImGuiCol_PopupBg: return "PopupBg";
3704
case ImGuiCol_Border: return "Border";
3705
case ImGuiCol_BorderShadow: return "BorderShadow";
3706
case ImGuiCol_FrameBg: return "FrameBg";
3707
case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
3708
case ImGuiCol_FrameBgActive: return "FrameBgActive";
3709
case ImGuiCol_TitleBg: return "TitleBg";
3710
case ImGuiCol_TitleBgActive: return "TitleBgActive";
3711
case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
3712
case ImGuiCol_MenuBarBg: return "MenuBarBg";
3713
case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
3714
case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
3715
case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
3716
case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
3717
case ImGuiCol_CheckMark: return "CheckMark";
3718
case ImGuiCol_SliderGrab: return "SliderGrab";
3719
case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
3720
case ImGuiCol_Button: return "Button";
3721
case ImGuiCol_ButtonHovered: return "ButtonHovered";
3722
case ImGuiCol_ButtonActive: return "ButtonActive";
3723
case ImGuiCol_Header: return "Header";
3724
case ImGuiCol_HeaderHovered: return "HeaderHovered";
3725
case ImGuiCol_HeaderActive: return "HeaderActive";
3726
case ImGuiCol_Separator: return "Separator";
3727
case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
3728
case ImGuiCol_SeparatorActive: return "SeparatorActive";
3729
case ImGuiCol_ResizeGrip: return "ResizeGrip";
3730
case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
3731
case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
3732
case ImGuiCol_InputTextCursor: return "InputTextCursor";
3733
case ImGuiCol_TabHovered: return "TabHovered";
3734
case ImGuiCol_Tab: return "Tab";
3735
case ImGuiCol_TabSelected: return "TabSelected";
3736
case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
3737
case ImGuiCol_TabDimmed: return "TabDimmed";
3738
case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
3739
case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
3740
case ImGuiCol_PlotLines: return "PlotLines";
3741
case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
3742
case ImGuiCol_PlotHistogram: return "PlotHistogram";
3743
case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
3744
case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
3745
case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
3746
case ImGuiCol_TableBorderLight: return "TableBorderLight";
3747
case ImGuiCol_TableRowBg: return "TableRowBg";
3748
case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
3749
case ImGuiCol_TextLink: return "TextLink";
3750
case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3751
case ImGuiCol_TreeLines: return "TreeLines";
3752
case ImGuiCol_DragDropTarget: return "DragDropTarget";
3753
case ImGuiCol_DragDropTargetBg: return "DragDropTargetBg";
3754
case ImGuiCol_UnsavedMarker: return "UnsavedMarker";
3755
case ImGuiCol_NavCursor: return "NavCursor";
3756
case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
3757
case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
3758
case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
3759
}
3760
IM_ASSERT(0);
3761
return "Unknown";
3762
}
3763
3764
//-----------------------------------------------------------------------------
3765
// [SECTION] RENDER HELPERS
3766
// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
3767
// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
3768
// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
3769
//-----------------------------------------------------------------------------
3770
3771
const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
3772
{
3773
const char* text_display_end = text;
3774
if (!text_end)
3775
text_end = (const char*)-1;
3776
3777
while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
3778
text_display_end++;
3779
return text_display_end;
3780
}
3781
3782
// Internal ImGui functions to render text
3783
// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3784
void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3785
{
3786
ImGuiContext& g = *GImGui;
3787
ImGuiWindow* window = g.CurrentWindow;
3788
3789
// Hide anything after a '##' string
3790
const char* text_display_end;
3791
if (hide_text_after_hash)
3792
{
3793
text_display_end = FindRenderedTextEnd(text, text_end);
3794
}
3795
else
3796
{
3797
if (!text_end)
3798
text_end = text + ImStrlen(text); // FIXME-OPT
3799
text_display_end = text_end;
3800
}
3801
3802
if (text != text_display_end)
3803
{
3804
window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3805
if (g.LogEnabled)
3806
LogRenderedText(&pos, text, text_display_end);
3807
}
3808
}
3809
3810
void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3811
{
3812
ImGuiContext& g = *GImGui;
3813
ImGuiWindow* window = g.CurrentWindow;
3814
3815
if (!text_end)
3816
text_end = text + ImStrlen(text); // FIXME-OPT
3817
3818
if (text != text_end)
3819
{
3820
window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3821
if (g.LogEnabled)
3822
LogRenderedText(&pos, text, text_end);
3823
}
3824
}
3825
3826
// Default clip_rect uses (pos_min,pos_max)
3827
// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3828
// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.
3829
// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
3830
// better advantage of the render function taking size into account for coarse clipping.
3831
void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
3832
{
3833
// Perform CPU side clipping for single clipped element to avoid using scissor state
3834
ImVec2 pos = pos_min;
3835
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3836
3837
const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3838
const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3839
bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3840
if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3841
need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3842
3843
// Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3844
if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3845
if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3846
3847
// Render
3848
if (need_clipping)
3849
{
3850
ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3851
draw_list->AddText(NULL, 0.0f, 0, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3852
}
3853
else
3854
{
3855
draw_list->AddText(NULL, 0.0f, 0, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3856
}
3857
}
3858
3859
void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
3860
{
3861
// Hide anything after a '##' string
3862
const char* text_display_end = FindRenderedTextEnd(text, text_end);
3863
const int text_len = (int)(text_display_end - text);
3864
if (text_len == 0)
3865
return;
3866
3867
ImGuiContext& g = *GImGui;
3868
ImGuiWindow* window = g.CurrentWindow;
3869
RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
3870
if (g.LogEnabled)
3871
LogRenderedText(&pos_min, text, text_display_end);
3872
}
3873
3874
// Another overly complex function until we reorganize everything into a nice all-in-one helper.
3875
// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.
3876
// 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.
3877
// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users.
3878
void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
3879
{
3880
ImGuiContext& g = *GImGui;
3881
if (text_end_full == NULL)
3882
text_end_full = FindRenderedTextEnd(text);
3883
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
3884
3885
//draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));
3886
//draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));
3887
3888
// FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
3889
if (text_size.x > pos_max.x - pos_min.x)
3890
{
3891
// Hello wo...
3892
// | | |
3893
// min max ellipsis_max
3894
// <-> this is generally some padding value
3895
3896
ImFont* font = draw_list->_Data->Font;
3897
const float font_size = draw_list->_Data->FontSize;
3898
const float font_scale = draw_list->_Data->FontScale;
3899
const float font_weight = draw_list->_Data->FontWeight;
3900
const char* text_end_ellipsis = NULL;
3901
ImFontBaked* baked = font->GetFontBaked(font_size, font_weight);
3902
const float ellipsis_width = baked->GetCharAdvance(font->EllipsisChar) * font_scale;
3903
3904
// We can now claim the space between pos_max.x and ellipsis_max.x
3905
const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
3906
float text_size_clipped_x = font->CalcTextSizeA(font_size, font_weight, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
3907
while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
3908
{
3909
// 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)
3910
text_end_ellipsis--;
3911
text_size_clipped_x -= font->CalcTextSizeA(font_size, font_weight, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
3912
}
3913
3914
// Render text, render ellipsis
3915
RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
3916
ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);
3917
ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
3918
font->RenderChar(draw_list, font_size, font_weight, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);
3919
}
3920
else
3921
{
3922
RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
3923
}
3924
3925
if (g.LogEnabled)
3926
LogRenderedText(&pos_min, text, text_end_full);
3927
}
3928
3929
// Render a rectangle shaped with optional rounding and borders
3930
void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)
3931
{
3932
ImGuiContext& g = *GImGui;
3933
ImGuiWindow* window = g.CurrentWindow;
3934
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3935
const float border_size = g.Style.FrameBorderSize;
3936
if (borders && border_size > 0.0f)
3937
{
3938
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3939
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3940
}
3941
}
3942
3943
void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3944
{
3945
ImGuiContext& g = *GImGui;
3946
ImGuiWindow* window = g.CurrentWindow;
3947
const float border_size = g.Style.FrameBorderSize;
3948
if (border_size > 0.0f)
3949
{
3950
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3951
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3952
}
3953
}
3954
3955
void ImGui::RenderColorComponentMarker(const ImRect& bb, ImU32 col, float rounding)
3956
{
3957
if (bb.Min.x + 1 >= bb.Max.x)
3958
return;
3959
ImGuiContext& g = *GImGui;
3960
ImGuiWindow* window = g.CurrentWindow;
3961
RenderRectFilledInRangeH(window->DrawList, bb, col, bb.Min.x, ImMin(bb.Min.x + g.Style.ColorMarkerSize, bb.Max.x), rounding);
3962
}
3963
3964
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
3965
{
3966
ImGuiContext& g = *GImGui;
3967
if (id != g.NavId)
3968
return;
3969
if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
3970
return;
3971
if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
3972
return;
3973
ImGuiWindow* window = g.CurrentWindow;
3974
if (window->DC.NavHideHighlightOneFrame)
3975
return;
3976
3977
float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
3978
ImRect display_rect = bb;
3979
display_rect.ClipWith(window->ClipRect);
3980
const float thickness = 2.0f;
3981
if (flags & ImGuiNavRenderCursorFlags_Compact)
3982
{
3983
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3984
}
3985
else
3986
{
3987
const float distance = 3.0f + thickness * 0.5f;
3988
display_rect.Expand(ImVec2(distance, distance));
3989
bool fully_visible = window->ClipRect.Contains(display_rect);
3990
if (!fully_visible)
3991
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
3992
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3993
if (!fully_visible)
3994
window->DrawList->PopClipRect();
3995
}
3996
}
3997
3998
void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
3999
{
4000
ImGuiContext& g = *GImGui;
4001
if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
4002
mouse_cursor = ImGuiMouseCursor_Arrow;
4003
ImFontAtlas* font_atlas = g.DrawListSharedData.FontAtlas;
4004
for (ImGuiViewportP* viewport : g.Viewports)
4005
{
4006
// We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
4007
ImVec2 offset, size, uv[4];
4008
if (!ImFontAtlasGetMouseCursorTexData(font_atlas, mouse_cursor, &offset, &size, &uv[0], &uv[2]))
4009
continue;
4010
const ImVec2 pos = base_pos - offset;
4011
const float scale = base_scale;
4012
if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
4013
continue;
4014
ImDrawList* draw_list = GetForegroundDrawList(viewport);
4015
ImTextureRef tex_ref = font_atlas->TexRef;
4016
draw_list->PushTexture(tex_ref);
4017
draw_list->AddImage(tex_ref, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
4018
draw_list->AddImage(tex_ref, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
4019
draw_list->AddImage(tex_ref, pos, pos + size * scale, uv[2], uv[3], col_border);
4020
draw_list->AddImage(tex_ref, pos, pos + size * scale, uv[0], uv[1], col_fill);
4021
if (mouse_cursor == ImGuiMouseCursor_Wait || mouse_cursor == ImGuiMouseCursor_Progress)
4022
{
4023
float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI);
4024
float a_max = a_min + IM_PI * 1.65f;
4025
draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max);
4026
draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale);
4027
}
4028
draw_list->PopTexture();
4029
}
4030
}
4031
4032
//-----------------------------------------------------------------------------
4033
// [SECTION] INITIALIZATION, SHUTDOWN
4034
//-----------------------------------------------------------------------------
4035
4036
// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
4037
// 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
4038
ImGuiContext* ImGui::GetCurrentContext()
4039
{
4040
return GImGui;
4041
}
4042
4043
void ImGui::SetCurrentContext(ImGuiContext* ctx)
4044
{
4045
#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
4046
IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
4047
#else
4048
GImGui = ctx;
4049
#endif
4050
}
4051
4052
void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
4053
{
4054
GImAllocatorAllocFunc = alloc_func;
4055
GImAllocatorFreeFunc = free_func;
4056
GImAllocatorUserData = user_data;
4057
}
4058
4059
// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
4060
void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
4061
{
4062
*p_alloc_func = GImAllocatorAllocFunc;
4063
*p_free_func = GImAllocatorFreeFunc;
4064
*p_user_data = GImAllocatorUserData;
4065
}
4066
4067
ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
4068
{
4069
ImGuiContext* prev_ctx = GetCurrentContext();
4070
ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
4071
SetCurrentContext(ctx);
4072
Initialize();
4073
if (prev_ctx != NULL)
4074
SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
4075
return ctx;
4076
}
4077
4078
void ImGui::DestroyContext(ImGuiContext* ctx)
4079
{
4080
ImGuiContext* prev_ctx = GetCurrentContext();
4081
if (ctx == NULL) //-V1051
4082
ctx = prev_ctx;
4083
SetCurrentContext(ctx);
4084
Shutdown();
4085
SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
4086
IM_DELETE(ctx);
4087
}
4088
4089
// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
4090
static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
4091
{
4092
{ ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
4093
{ ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
4094
{ ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
4095
{ ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
4096
{ ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" },
4097
{ ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" },
4098
{ ImGuiLocKey_WindowingPopup, "(Popup)" },
4099
{ ImGuiLocKey_WindowingUntitled, "(Untitled)" },
4100
{ ImGuiLocKey_OpenLink_s, "Open '%s'" },
4101
{ ImGuiLocKey_CopyLink, "Copy Link###CopyLink" },
4102
};
4103
4104
ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
4105
{
4106
IO.Ctx = this;
4107
InputTextState.Ctx = this;
4108
4109
Initialized = false;
4110
WithinFrameScope = WithinFrameScopeWithImplicitWindow = false;
4111
TestEngineHookItems = false;
4112
FrameCount = 0;
4113
FrameCountEnded = FrameCountRendered = -1;
4114
Time = 0.0f;
4115
memset(ContextName, 0, sizeof(ContextName));
4116
4117
Font = NULL;
4118
FontBaked = NULL;
4119
FontSize = FontSizeBase = FontBakedScale = FontWeight = CurrentDpiScale = 0.0f;
4120
FontRasterizerDensity = 1.0f;
4121
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
4122
if (shared_font_atlas == NULL)
4123
IO.Fonts->OwnerContext = this;
4124
WithinEndChildID = 0;
4125
TestEngine = NULL;
4126
4127
InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
4128
InputEventsNextEventId = 1;
4129
4130
WindowsActiveCount = 0;
4131
WindowsBorderHoverPadding = 0.0f;
4132
CurrentWindow = NULL;
4133
HoveredWindow = NULL;
4134
HoveredWindowUnderMovingWindow = NULL;
4135
HoveredWindowBeforeClear = NULL;
4136
MovingWindow = NULL;
4137
WheelingWindow = NULL;
4138
WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
4139
WheelingWindowReleaseTimer = 0.0f;
4140
4141
DebugDrawIdConflictsId = 0;
4142
DebugHookIdInfoId = 0;
4143
HoveredId = HoveredIdPreviousFrame = 0;
4144
HoveredIdPreviousFrameItemCount = 0;
4145
HoveredIdAllowOverlap = false;
4146
HoveredIdIsDisabled = false;
4147
HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
4148
ItemUnclipByLog = false;
4149
ActiveId = 0;
4150
ActiveIdIsAlive = 0;
4151
ActiveIdTimer = 0.0f;
4152
ActiveIdIsJustActivated = false;
4153
ActiveIdAllowOverlap = false;
4154
ActiveIdNoClearOnFocusLoss = false;
4155
ActiveIdHasBeenPressedBefore = false;
4156
ActiveIdHasBeenEditedBefore = false;
4157
ActiveIdHasBeenEditedThisFrame = false;
4158
ActiveIdFromShortcut = false;
4159
ActiveIdClickOffset = ImVec2(-1, -1);
4160
ActiveIdSource = ImGuiInputSource_None;
4161
ActiveIdWindow = NULL;
4162
ActiveIdMouseButton = -1;
4163
ActiveIdDisabledId = 0;
4164
ActiveIdPreviousFrame = 0;
4165
memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData));
4166
memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
4167
LastActiveId = 0;
4168
LastActiveIdTimer = 0.0f;
4169
4170
LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
4171
4172
ActiveIdUsingNavDirMask = 0x00;
4173
ActiveIdUsingAllKeyboardKeys = false;
4174
4175
CurrentFocusScopeId = 0;
4176
CurrentItemFlags = ImGuiItemFlags_None;
4177
DebugShowGroupRects = false;
4178
GcCompactAll = false;
4179
4180
NavCursorVisible = false;
4181
NavHighlightItemUnderNav = false;
4182
NavMousePosDirty = false;
4183
NavIdIsAlive = false;
4184
NavId = 0;
4185
NavWindow = NULL;
4186
NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
4187
NavLayer = ImGuiNavLayer_Main;
4188
NavNextActivateId = 0;
4189
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
4190
NavHighlightActivatedId = 0;
4191
NavHighlightActivatedTimer = 0.0f;
4192
NavInputSource = ImGuiInputSource_Keyboard;
4193
NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
4194
NavCursorHideFrames = 0;
4195
4196
NavAnyRequest = false;
4197
NavInitRequest = false;
4198
NavInitRequestFromMove = false;
4199
NavMoveSubmitted = false;
4200
NavMoveScoringItems = false;
4201
NavMoveForwardToNextFrame = false;
4202
NavMoveFlags = ImGuiNavMoveFlags_None;
4203
NavMoveScrollFlags = ImGuiScrollFlags_None;
4204
NavMoveKeyMods = ImGuiMod_None;
4205
NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
4206
NavScoringDebugCount = 0;
4207
NavTabbingDir = 0;
4208
NavTabbingCounter = 0;
4209
4210
NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
4211
NavJustMovedToKeyMods = ImGuiMod_None;
4212
NavJustMovedToIsTabbing = false;
4213
NavJustMovedToHasSelectionData = false;
4214
4215
// All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
4216
// FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
4217
ConfigNavWindowingWithGamepad = true;
4218
ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
4219
ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
4220
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
4221
NavWindowingInputSource = ImGuiInputSource_None;
4222
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
4223
NavWindowingToggleLayer = false;
4224
NavWindowingToggleKey = ImGuiKey_None;
4225
4226
DimBgRatio = 0.0f;
4227
4228
DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
4229
DragDropSourceFlags = ImGuiDragDropFlags_None;
4230
DragDropSourceFrameCount = -1;
4231
DragDropMouseButton = -1;
4232
DragDropTargetId = 0;
4233
DragDropTargetFullViewport = 0;
4234
DragDropAcceptFlagsCurr = DragDropAcceptFlagsPrev = ImGuiDragDropFlags_None;
4235
DragDropAcceptIdCurrRectSurface = 0.0f;
4236
DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
4237
DragDropAcceptFrameCount = -1;
4238
DragDropHoldJustPressedId = 0;
4239
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
4240
4241
ClipperTempDataStacked = 0;
4242
4243
CurrentTable = NULL;
4244
TablesTempDataStacked = 0;
4245
CurrentTabBar = NULL;
4246
CurrentMultiSelect = NULL;
4247
MultiSelectTempDataStacked = 0;
4248
4249
HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
4250
HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
4251
4252
MouseCursor = ImGuiMouseCursor_Arrow;
4253
MouseStationaryTimer = 0.0f;
4254
4255
InputTextPasswordFontBackupFlags = ImFontFlags_None;
4256
TempInputId = 0;
4257
memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
4258
BeginMenuDepth = BeginComboDepth = 0;
4259
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
4260
ColorEditCurrentID = ColorEditSavedID = 0;
4261
ColorEditSavedHue = ColorEditSavedSat = 0.0f;
4262
ColorEditSavedColor = 0;
4263
WindowResizeRelativeMode = false;
4264
ScrollbarHeld = false;
4265
ScrollbarSeekMode = 0;
4266
ScrollbarClickDeltaToGrabCenter = 0.0f;
4267
SliderGrabClickOffset = 0.0f;
4268
SliderCurrentAccum = 0.0f;
4269
SliderCurrentAccumDirty = false;
4270
DragCurrentAccumDirty = false;
4271
DragCurrentAccum = 0.0f;
4272
DragSpeedDefaultRatio = 1.0f / 100.0f;
4273
DisabledAlphaBackup = 0.0f;
4274
DisabledStackSize = 0;
4275
TooltipOverrideCount = 0;
4276
TooltipPreviousWindow = NULL;
4277
4278
PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
4279
PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
4280
4281
SettingsLoaded = false;
4282
SettingsDirtyTimer = 0.0f;
4283
HookIdNext = 0;
4284
4285
memset(LocalizationTable, 0, sizeof(LocalizationTable));
4286
4287
LogEnabled = false;
4288
LogLineFirstItem = false;
4289
LogFlags = ImGuiLogFlags_None;
4290
LogWindow = NULL;
4291
LogNextPrefix = LogNextSuffix = NULL;
4292
LogFile = NULL;
4293
LogLinePosY = FLT_MAX;
4294
LogDepthRef = 0;
4295
LogDepthToExpand = LogDepthToExpandDefault = 2;
4296
4297
ErrorCallback = NULL;
4298
ErrorCallbackUserData = NULL;
4299
ErrorFirst = true;
4300
ErrorCountCurrentFrame = 0;
4301
StackSizesInBeginForCurrentWindow = NULL;
4302
4303
DebugDrawIdConflictsCount = 0;
4304
DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
4305
DebugLocateId = 0;
4306
DebugLogSkippedErrors = 0;
4307
DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
4308
DebugLogAutoDisableFrames = 0;
4309
DebugLocateFrames = 0;
4310
DebugBeginReturnValueCullDepth = -1;
4311
DebugItemPickerActive = false;
4312
DebugItemPickerMouseButton = ImGuiMouseButton_Left;
4313
DebugItemPickerBreakId = 0;
4314
DebugFlashStyleColorTime = 0.0f;
4315
DebugFlashStyleColorIdx = ImGuiCol_COUNT;
4316
4317
// Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
4318
DebugBreakInWindow = 0;
4319
DebugBreakInTable = 0;
4320
DebugBreakInLocateId = false;
4321
DebugBreakKeyChord = ImGuiKey_Pause;
4322
DebugBreakInShortcutRouting = ImGuiKey_None;
4323
4324
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
4325
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
4326
FramerateSecPerFrameAccum = 0.0f;
4327
WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
4328
memset(TempKeychordName, 0, sizeof(TempKeychordName));
4329
}
4330
4331
ImGuiContext::~ImGuiContext()
4332
{
4333
IM_ASSERT(Initialized == false && "Forgot to call DestroyContext()?");
4334
}
4335
4336
void ImGui::Initialize()
4337
{
4338
ImGuiContext& g = *GImGui;
4339
IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4340
4341
// Add .ini handle for ImGuiWindow and ImGuiTable types
4342
{
4343
ImGuiSettingsHandler ini_handler;
4344
ini_handler.TypeName = "Window";
4345
ini_handler.TypeHash = ImHashStr("Window");
4346
ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4347
ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4348
ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4349
ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4350
ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4351
AddSettingsHandler(&ini_handler);
4352
}
4353
TableSettingsAddSettingsHandler();
4354
4355
// Setup default localization table
4356
LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_COUNTOF(GLocalizationEntriesEnUS));
4357
4358
// Setup default ImGuiPlatformIO clipboard/IME handlers.
4359
g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
4360
g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;
4361
g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
4362
g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
4363
4364
// Create default viewport
4365
ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4366
viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4367
g.Viewports.push_back(viewport);
4368
g.TempBuffer.resize(1024 * 3 + 1, 0);
4369
4370
// Build KeysMayBeCharInput[] lookup table (1 bit per named key)
4371
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
4372
if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
4373
|| key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
4374
|| key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
4375
|| key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
4376
g.KeysMayBeCharInput.SetBit(key);
4377
4378
#ifdef IMGUI_HAS_DOCK
4379
#endif
4380
4381
// Print a debug message when running with debug feature IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS because it is very slow.
4382
// DO NOT COMMENT OUT THIS MESSAGE. IT IS DESIGNED TO REMIND YOU THAT IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS SHOULD ONLY BE TEMPORARILY ENABLED.
4383
#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
4384
DebugLog("IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n");
4385
#endif
4386
4387
// ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context.
4388
// But this link allows us to facilitate/handle a few edge cases better.
4389
ImFontAtlas* atlas = g.IO.Fonts;
4390
g.DrawListSharedData.Context = &g;
4391
RegisterFontAtlas(atlas);
4392
4393
g.Initialized = true;
4394
}
4395
4396
// This function is merely here to free heap allocations.
4397
void ImGui::Shutdown()
4398
{
4399
ImGuiContext& g = *GImGui;
4400
IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
4401
IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
4402
4403
// 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)
4404
for (ImFontAtlas* atlas : g.FontAtlases)
4405
{
4406
UnregisterFontAtlas(atlas);
4407
if (atlas->RefCount == 0)
4408
{
4409
atlas->Locked = false;
4410
IM_DELETE(atlas);
4411
}
4412
}
4413
g.DrawListSharedData.TempBuffer.clear();
4414
4415
// Cleanup of other data are conditional on actually having initialized Dear ImGui.
4416
if (!g.Initialized)
4417
return;
4418
4419
// Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4420
if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4421
SaveIniSettingsToDisk(g.IO.IniFilename);
4422
4423
CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4424
4425
// Clear everything else
4426
g.Windows.clear_delete();
4427
g.WindowsFocusOrder.clear();
4428
g.WindowsTempSortBuffer.clear();
4429
g.CurrentWindow = NULL;
4430
g.CurrentWindowStack.clear();
4431
g.WindowsById.Clear();
4432
g.NavWindow = NULL;
4433
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4434
g.ActiveIdWindow = NULL;
4435
g.MovingWindow = NULL;
4436
4437
g.KeysRoutingTable.Clear();
4438
4439
g.ColorStack.clear();
4440
g.StyleVarStack.clear();
4441
g.FontStack.clear();
4442
g.OpenPopupStack.clear();
4443
g.BeginPopupStack.clear();
4444
g.TreeNodeStack.clear();
4445
4446
g.Viewports.clear_delete();
4447
4448
g.TabBars.Clear();
4449
g.CurrentTabBarStack.clear();
4450
g.ShrinkWidthBuffer.clear();
4451
4452
g.ClipperTempData.clear_destruct();
4453
4454
g.Tables.Clear();
4455
g.TablesTempData.clear_destruct();
4456
g.DrawChannelsTempMergeBuffer.clear();
4457
4458
g.MultiSelectStorage.Clear();
4459
g.MultiSelectTempData.clear_destruct();
4460
4461
g.ClipboardHandlerData.clear();
4462
g.MenusIdSubmittedThisFrame.clear();
4463
g.InputTextState.ClearFreeMemory();
4464
g.InputTextLineIndex.clear();
4465
g.InputTextDeactivatedState.ClearFreeMemory();
4466
4467
g.SettingsWindows.clear();
4468
g.SettingsHandlers.clear();
4469
4470
if (g.LogFile)
4471
{
4472
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4473
if (g.LogFile != stdout)
4474
#endif
4475
ImFileClose(g.LogFile);
4476
g.LogFile = NULL;
4477
}
4478
g.LogBuffer.clear();
4479
g.DebugLogBuf.clear();
4480
g.DebugLogIndex.clear();
4481
4482
g.Initialized = false;
4483
}
4484
4485
// When using multiple context it can be helpful to give name a name.
4486
// (A) Will be visible in debugger, (B) Will be included in all IMGUI_DEBUG_LOG() calls, (C) Should be <= 15 characters long.
4487
void ImGui::SetContextName(ImGuiContext* ctx, const char* name)
4488
{
4489
ImStrncpy(ctx->ContextName, name, IM_COUNTOF(ctx->ContextName));
4490
}
4491
4492
// No specific ordering/dependency support, will see as needed
4493
ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
4494
{
4495
ImGuiContext& g = *ctx;
4496
IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
4497
g.Hooks.push_back(*hook);
4498
g.Hooks.back().HookId = ++g.HookIdNext;
4499
return g.HookIdNext;
4500
}
4501
4502
// Deferred removal, avoiding issue with changing vector while iterating it
4503
void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
4504
{
4505
ImGuiContext& g = *ctx;
4506
IM_ASSERT(hook_id != 0);
4507
for (ImGuiContextHook& hook : g.Hooks)
4508
if (hook.HookId == hook_id)
4509
hook.Type = ImGuiContextHookType_PendingRemoval_;
4510
}
4511
4512
// Call context hooks (used by e.g. test engine)
4513
// We assume a small number of hooks so all stored in same array
4514
void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
4515
{
4516
ImGuiContext& g = *ctx;
4517
for (ImGuiContextHook& hook : g.Hooks)
4518
if (hook.Type == hook_type)
4519
hook.Callback(&g, &hook);
4520
}
4521
4522
//-----------------------------------------------------------------------------
4523
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
4524
//-----------------------------------------------------------------------------
4525
4526
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
4527
ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
4528
{
4529
memset(this, 0, sizeof(*this));
4530
Ctx = ctx;
4531
Name = ImStrdup(name);
4532
NameBufLen = (int)ImStrlen(name) + 1;
4533
ID = ImHashStr(name);
4534
IDStack.push_back(ID);
4535
MoveId = GetID("#MOVE");
4536
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4537
ScrollExpected = ImVec2(0, 0);
4538
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
4539
AutoPosLastDirection = ImGuiDir_None;
4540
AutoFitFramesX = AutoFitFramesY = -1;
4541
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0;
4542
SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
4543
LastFrameActive = -1;
4544
LastTimeActive = -1.0f;
4545
FontRefSize = 0.0f;
4546
FontWindowScale = FontWindowScaleParents = 1.0f;
4547
SettingsOffset = -1;
4548
DrawList = &DrawListInst;
4549
DrawList->_OwnerName = Name;
4550
DrawList->_SetDrawListSharedData(&Ctx->DrawListSharedData);
4551
NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
4552
}
4553
4554
ImGuiWindow::~ImGuiWindow()
4555
{
4556
IM_ASSERT(DrawList == &DrawListInst);
4557
IM_DELETE(Name);
4558
ColumnsStorage.clear_destruct();
4559
}
4560
4561
static void SetCurrentWindow(ImGuiWindow* window)
4562
{
4563
ImGuiContext& g = *GImGui;
4564
g.CurrentWindow = window;
4565
g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
4566
g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
4567
g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
4568
if (window)
4569
{
4570
bool backup_skip_items = window->SkipItems;
4571
window->SkipItems = false;
4572
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
4573
{
4574
ImGuiViewport* viewport = window->Viewport;
4575
g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity()
4576
}
4577
ImGui::UpdateCurrentFontSize(0.0f);
4578
window->SkipItems = backup_skip_items;
4579
ImGui::NavUpdateCurrentWindowIsScrollPushableX();
4580
}
4581
}
4582
4583
void ImGui::GcCompactTransientMiscBuffers()
4584
{
4585
ImGuiContext& g = *GImGui;
4586
g.ItemFlagsStack.clear();
4587
g.GroupStack.clear();
4588
g.InputTextLineIndex.clear();
4589
g.MultiSelectTempDataStacked = 0;
4590
g.MultiSelectTempData.clear_destruct();
4591
TableGcCompactSettings();
4592
for (ImFontAtlas* atlas : g.FontAtlases)
4593
atlas->CompactCache();
4594
}
4595
4596
// Free up/compact internal window buffers, we can use this when a window becomes unused.
4597
// Not freed:
4598
// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
4599
// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
4600
void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
4601
{
4602
window->MemoryCompacted = true;
4603
window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
4604
window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
4605
window->IDStack.clear();
4606
window->DrawList->_ClearFreeMemory();
4607
window->DC.ChildWindows.clear();
4608
window->DC.ItemWidthStack.clear();
4609
window->DC.TextWrapPosStack.clear();
4610
}
4611
4612
void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
4613
{
4614
// We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
4615
// The other buffers tends to amortize much faster.
4616
window->MemoryCompacted = false;
4617
window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
4618
window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
4619
window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
4620
}
4621
4622
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
4623
{
4624
ImGuiContext& g = *GImGui;
4625
4626
// Clear previous active id
4627
if (g.ActiveId != 0)
4628
{
4629
// Store deactivate data
4630
ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData;
4631
deactivated_data->ID = g.ActiveId;
4632
deactivated_data->ElapseFrame = (g.LastItemData.ID == g.ActiveId) ? g.FrameCount : g.FrameCount + 1; // FIXME: OK to use LastItemData?
4633
deactivated_data->HasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4634
deactivated_data->IsAlive = (g.ActiveIdIsAlive == g.ActiveId);
4635
4636
// This could be written in a more general way (e.g associate a hook to ActiveId),
4637
// but since this is currently quite an exception we'll leave it as is.
4638
// One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()
4639
if (g.InputTextState.ID == g.ActiveId)
4640
InputTextDeactivateHook(g.ActiveId);
4641
4642
// While most behaved code would make an effort to not steal active id during window move/drag operations,
4643
// we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
4644
// may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
4645
if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
4646
{
4647
IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
4648
StopMouseMovingWindow();
4649
}
4650
}
4651
4652
// Set active id
4653
g.ActiveIdIsJustActivated = (g.ActiveId != id);
4654
if (g.ActiveIdIsJustActivated)
4655
{
4656
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 : "");
4657
g.ActiveIdTimer = 0.0f;
4658
g.ActiveIdHasBeenPressedBefore = false;
4659
g.ActiveIdHasBeenEditedBefore = false;
4660
g.ActiveIdMouseButton = -1;
4661
if (id != 0)
4662
{
4663
g.LastActiveId = id;
4664
g.LastActiveIdTimer = 0.0f;
4665
}
4666
}
4667
g.ActiveId = id;
4668
g.ActiveIdAllowOverlap = false;
4669
g.ActiveIdNoClearOnFocusLoss = false;
4670
g.ActiveIdWindow = window;
4671
g.ActiveIdHasBeenEditedThisFrame = false;
4672
g.ActiveIdFromShortcut = false;
4673
g.ActiveIdDisabledId = 0;
4674
if (id)
4675
{
4676
g.ActiveIdIsAlive = id;
4677
g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
4678
IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
4679
}
4680
4681
// Clear declaration of inputs claimed by the widget
4682
// (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
4683
g.ActiveIdUsingNavDirMask = 0x00;
4684
g.ActiveIdUsingAllKeyboardKeys = false;
4685
}
4686
4687
void ImGui::ClearActiveID()
4688
{
4689
SetActiveID(0, NULL); // g.ActiveId = 0;
4690
}
4691
4692
void ImGui::SetHoveredID(ImGuiID id)
4693
{
4694
ImGuiContext& g = *GImGui;
4695
g.HoveredId = id;
4696
g.HoveredIdAllowOverlap = false;
4697
if (id != 0 && g.HoveredIdPreviousFrame != id)
4698
g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
4699
}
4700
4701
ImGuiID ImGui::GetHoveredID()
4702
{
4703
ImGuiContext& g = *GImGui;
4704
return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
4705
}
4706
4707
void ImGui::MarkItemEdited(ImGuiID id)
4708
{
4709
// This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
4710
// 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.
4711
ImGuiContext& g = *GImGui;
4712
if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
4713
return;
4714
if (g.ActiveId == id || g.ActiveId == 0)
4715
{
4716
// FIXME: Can't we fully rely on LastItemData yet?
4717
g.ActiveIdHasBeenEditedThisFrame = true;
4718
g.ActiveIdHasBeenEditedBefore = true;
4719
if (g.DeactivatedItemData.ID == id)
4720
g.DeactivatedItemData.HasBeenEditedBefore = true;
4721
}
4722
4723
// We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
4724
// We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
4725
// FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction.
4726
IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
4727
4728
//IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
4729
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
4730
}
4731
4732
bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
4733
{
4734
// An active popup disable hovering on other windows (apart from its own children)
4735
// FIXME-OPT: This could be cached/stored within the window.
4736
ImGuiContext& g = *GImGui;
4737
if (g.NavWindow)
4738
if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
4739
if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
4740
{
4741
// For the purpose of those flags we differentiate "standard popup" from "modal popup"
4742
// NB: The 'else' is important because Modal windows are also Popups.
4743
bool want_inhibit = false;
4744
if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
4745
want_inhibit = true;
4746
else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
4747
want_inhibit = true;
4748
4749
// Inhibit hover unless the window is within the stack of our modal/popup
4750
if (want_inhibit)
4751
if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
4752
return false;
4753
}
4754
return true;
4755
}
4756
4757
static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
4758
{
4759
ImGuiContext& g = *GImGui;
4760
if (flags & ImGuiHoveredFlags_DelayNormal)
4761
return g.Style.HoverDelayNormal;
4762
if (flags & ImGuiHoveredFlags_DelayShort)
4763
return g.Style.HoverDelayShort;
4764
return 0.0f;
4765
}
4766
4767
static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
4768
{
4769
// Allow instance flags to override shared flags
4770
if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
4771
shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
4772
return user_flags | shared_flags;
4773
}
4774
4775
// This is roughly matching the behavior of internal-facing ItemHoverable()
4776
// - 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()
4777
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
4778
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
4779
{
4780
ImGuiContext& g = *GImGui;
4781
ImGuiWindow* window = g.CurrentWindow;
4782
IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
4783
4784
if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
4785
{
4786
if (!IsItemFocused())
4787
return false;
4788
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4789
return false;
4790
4791
if (flags & ImGuiHoveredFlags_ForTooltip)
4792
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
4793
}
4794
else
4795
{
4796
// Test for bounding box overlap, as updated as ItemAdd()
4797
ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
4798
if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
4799
return false;
4800
4801
if (flags & ImGuiHoveredFlags_ForTooltip)
4802
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
4803
4804
// Done with rectangle culling so we can perform heavier checks now
4805
// Test if we are hovering the right window (our window could be behind another window)
4806
// [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
4807
// [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
4808
// 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
4809
// the test that has been running for a long while.
4810
if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
4811
if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
4812
return false;
4813
4814
// Test if another item is active (e.g. being dragged)
4815
const ImGuiID id = g.LastItemData.ID;
4816
if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
4817
if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4818
{
4819
// When ActiveId == MoveId it means that either:
4820
// - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag)
4821
// - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). // FIXME: May be fixed.
4822
// - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId.
4823
bool cancel_is_hovered = true;
4824
if (g.ActiveId == window->MoveId && (id == 0 || g.ActiveIdDisabledId == id))
4825
cancel_is_hovered = false;
4826
if (cancel_is_hovered)
4827
return false;
4828
}
4829
4830
// Test if interactions on this window are blocked by an active popup or modal.
4831
// The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
4832
if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
4833
return false;
4834
4835
// Test if the item is disabled
4836
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4837
return false;
4838
4839
// Special handling for calling after Begin() which represent the title bar or tab.
4840
// When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
4841
// will never be overwritten so we need to detect the case.
4842
if (id == window->MoveId && window->WriteAccessed)
4843
return false;
4844
4845
// Test if using AllowOverlap and overlapped
4846
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
4847
if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
4848
if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
4849
return false;
4850
}
4851
4852
// Handle hover delay
4853
// (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
4854
const float delay = CalcDelayFromHoveredFlags(flags);
4855
if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
4856
{
4857
ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min);
4858
if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
4859
g.HoverItemDelayTimer = 0.0f;
4860
g.HoverItemDelayId = hover_delay_id;
4861
4862
// When changing hovered item we requires a bit of stationary delay before activating hover timer,
4863
// but once unlocked on a given item we also moving.
4864
//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); }
4865
if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
4866
return false;
4867
4868
if (g.HoverItemDelayTimer < delay)
4869
return false;
4870
}
4871
4872
return true;
4873
}
4874
4875
// Internal facing ItemHoverable() used when submitting widgets. THIS IS A SUBMISSION NOT A HOVER CHECK.
4876
// Returns whether the item was hovered, logic differs slightly from IsItemHovered().
4877
// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
4878
// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
4879
// If you used this in your legacy/custom widgets code:
4880
// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
4881
// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
4882
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
4883
{
4884
ImGuiContext& g = *GImGui;
4885
ImGuiWindow* window = g.CurrentWindow;
4886
4887
// Detect ID conflicts
4888
// (this is specifically done here by comparing on hover because it allows us a detection of duplicates that is algorithmically extra cheap, 1 u32 compare per item. No O(log N) lookup whatsoever)
4889
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4890
if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
4891
{
4892
g.HoveredIdPreviousFrameItemCount++;
4893
if (g.DebugDrawIdConflictsId == id)
4894
window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
4895
}
4896
#endif
4897
4898
if (g.HoveredWindow != window)
4899
return false;
4900
if (!IsMouseHoveringRect(bb.Min, bb.Max))
4901
return false;
4902
4903
if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
4904
return false;
4905
if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4906
if (!g.ActiveIdFromShortcut)
4907
return false;
4908
4909
// We are done with rectangle culling so we can perform heavier checks now.
4910
if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
4911
{
4912
g.HoveredIdIsDisabled = true;
4913
return false;
4914
}
4915
4916
// We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
4917
// hover test in widgets code. We could also decide to split this function is two.
4918
if (id != 0)
4919
{
4920
// Drag source doesn't report as hovered
4921
if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
4922
return false;
4923
4924
SetHoveredID(id);
4925
4926
// AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
4927
// This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
4928
if (item_flags & ImGuiItemFlags_AllowOverlap)
4929
{
4930
g.HoveredIdAllowOverlap = true;
4931
if (g.HoveredIdPreviousFrame != id)
4932
return false;
4933
}
4934
4935
// Display shortcut (only works with mouse)
4936
// (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
4937
if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
4938
if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
4939
SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
4940
}
4941
4942
// When disabled we'll return false but still set HoveredId
4943
if (item_flags & ImGuiItemFlags_Disabled)
4944
{
4945
// Release active id if turning disabled
4946
if (g.ActiveId == id && id != 0)
4947
ClearActiveID();
4948
g.HoveredIdIsDisabled = true;
4949
return false;
4950
}
4951
4952
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4953
if (id != 0)
4954
{
4955
// [DEBUG] Item Picker tool!
4956
// We perform the check here because reaching is path is rare (1~ time a frame),
4957
// making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
4958
// items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
4959
if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
4960
GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
4961
if (g.DebugItemPickerBreakId == id)
4962
IM_DEBUG_BREAK();
4963
}
4964
#endif
4965
4966
if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
4967
return false;
4968
4969
return true;
4970
}
4971
4972
// FIXME: This is inlined/duplicated in ItemAdd()
4973
// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
4974
bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
4975
{
4976
ImGuiContext& g = *GImGui;
4977
ImGuiWindow* window = g.CurrentWindow;
4978
if (!bb.Overlaps(window->ClipRect))
4979
if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
4980
if (!g.ItemUnclipByLog)
4981
return true;
4982
return false;
4983
}
4984
4985
// This is also inlined in ItemAdd()
4986
// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
4987
void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect)
4988
{
4989
ImGuiContext& g = *GImGui;
4990
g.LastItemData.ID = item_id;
4991
g.LastItemData.ItemFlags = item_flags;
4992
g.LastItemData.StatusFlags = status_flags;
4993
g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
4994
}
4995
4996
static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
4997
{
4998
ImGuiContext& g = *GImGui;
4999
SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect);
5000
}
5001
5002
static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect)
5003
{
5004
ImGuiContext& g = *GImGui;
5005
SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect);
5006
}
5007
5008
float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
5009
{
5010
if (wrap_pos_x < 0.0f)
5011
return 0.0f;
5012
5013
ImGuiContext& g = *GImGui;
5014
ImGuiWindow* window = g.CurrentWindow;
5015
if (wrap_pos_x == 0.0f)
5016
{
5017
// We could decide to setup a default wrapping max point for auto-resizing windows,
5018
// or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
5019
//if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
5020
// wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
5021
//else
5022
wrap_pos_x = window->WorkRect.Max.x;
5023
}
5024
else if (wrap_pos_x > 0.0f)
5025
{
5026
wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
5027
}
5028
5029
return ImMax(wrap_pos_x - pos.x, 1.0f);
5030
}
5031
5032
// IM_ALLOC() == ImGui::MemAlloc()
5033
void* ImGui::MemAlloc(size_t size)
5034
{
5035
void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
5036
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5037
if (ImGuiContext* ctx = GImGui)
5038
DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
5039
#endif
5040
return ptr;
5041
}
5042
5043
// IM_FREE() == ImGui::MemFree()
5044
void ImGui::MemFree(void* ptr)
5045
{
5046
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5047
if (ptr != NULL)
5048
if (ImGuiContext* ctx = GImGui)
5049
DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
5050
#endif
5051
return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
5052
}
5053
5054
// 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"
5055
void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
5056
{
5057
ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
5058
IM_UNUSED(ptr);
5059
if (entry->FrameCount != frame_count)
5060
{
5061
info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_COUNTOF(info->LastEntriesBuf);
5062
entry = &info->LastEntriesBuf[info->LastEntriesIdx];
5063
entry->FrameCount = frame_count;
5064
entry->AllocCount = entry->FreeCount = 0;
5065
}
5066
if (size != (size_t)-1)
5067
{
5068
//printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, (int)size, ptr);
5069
entry->AllocCount++;
5070
info->TotalAllocCount++;
5071
}
5072
else
5073
{
5074
//printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
5075
entry->FreeCount++;
5076
info->TotalFreeCount++;
5077
}
5078
}
5079
5080
const char* ImGui::GetClipboardText()
5081
{
5082
ImGuiContext& g = *GImGui;
5083
return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : "";
5084
}
5085
5086
void ImGui::SetClipboardText(const char* text)
5087
{
5088
ImGuiContext& g = *GImGui;
5089
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
5090
g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
5091
}
5092
5093
const char* ImGui::GetVersion()
5094
{
5095
return IMGUI_VERSION;
5096
}
5097
5098
ImGuiIO& ImGui::GetIO()
5099
{
5100
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5101
return GImGui->IO;
5102
}
5103
5104
// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
5105
ImGuiIO& ImGui::GetIO(ImGuiContext* ctx)
5106
{
5107
IM_ASSERT(ctx != NULL);
5108
return ctx->IO;
5109
}
5110
5111
ImGuiPlatformIO& ImGui::GetPlatformIO()
5112
{
5113
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
5114
return GImGui->PlatformIO;
5115
}
5116
5117
// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
5118
ImGuiPlatformIO& ImGui::GetPlatformIO(ImGuiContext* ctx)
5119
{
5120
IM_ASSERT(ctx != NULL);
5121
return ctx->PlatformIO;
5122
}
5123
5124
// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
5125
ImDrawData* ImGui::GetDrawData()
5126
{
5127
ImGuiContext& g = *GImGui;
5128
ImGuiViewportP* viewport = g.Viewports[0];
5129
return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
5130
}
5131
5132
double ImGui::GetTime()
5133
{
5134
return GImGui->Time;
5135
}
5136
5137
int ImGui::GetFrameCount()
5138
{
5139
return GImGui->FrameCount;
5140
}
5141
5142
static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
5143
{
5144
// Create the draw list on demand, because they are not frequently used for all viewports
5145
ImGuiContext& g = *GImGui;
5146
IM_ASSERT(drawlist_no < IM_COUNTOF(viewport->BgFgDrawLists));
5147
ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
5148
if (draw_list == NULL)
5149
{
5150
draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
5151
draw_list->_OwnerName = drawlist_name;
5152
viewport->BgFgDrawLists[drawlist_no] = draw_list;
5153
}
5154
5155
// Our ImDrawList system requires that there is always a command
5156
if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
5157
{
5158
draw_list->_ResetForNewFrame();
5159
draw_list->PushTexture(g.IO.Fonts->TexRef);
5160
draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
5161
viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
5162
}
5163
return draw_list;
5164
}
5165
5166
ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
5167
{
5168
return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
5169
}
5170
5171
ImDrawList* ImGui::GetBackgroundDrawList()
5172
{
5173
ImGuiContext& g = *GImGui;
5174
return GetBackgroundDrawList(g.Viewports[0]);
5175
}
5176
5177
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
5178
{
5179
return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
5180
}
5181
5182
ImDrawList* ImGui::GetForegroundDrawList()
5183
{
5184
ImGuiContext& g = *GImGui;
5185
return GetForegroundDrawList(g.Viewports[0]);
5186
}
5187
5188
ImDrawListSharedData* ImGui::GetDrawListSharedData()
5189
{
5190
return &GImGui->DrawListSharedData;
5191
}
5192
5193
void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
5194
{
5195
// Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
5196
// We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
5197
// This is because we want ActiveId to be set even when the window is not permitted to move.
5198
ImGuiContext& g = *GImGui;
5199
FocusWindow(window);
5200
SetActiveID(window->MoveId, window);
5201
if (g.IO.ConfigNavCursorVisibleAuto)
5202
g.NavCursorVisible = false;
5203
g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
5204
g.ActiveIdNoClearOnFocusLoss = true;
5205
SetActiveIdUsingAllKeyboardKeys();
5206
5207
bool can_move_window = true;
5208
if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
5209
can_move_window = false;
5210
if (can_move_window)
5211
g.MovingWindow = window;
5212
}
5213
5214
// This is not 100% symetric with StartMouseMovingWindow().
5215
// We do NOT clear ActiveID, because:
5216
// - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired.
5217
// - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item).
5218
void ImGui::StopMouseMovingWindow()
5219
{
5220
ImGuiContext& g = *GImGui;
5221
5222
// [nb: docking branch has more stuff in this function]
5223
5224
g.MovingWindow = NULL;
5225
}
5226
5227
// Handle mouse moving window
5228
// Note: moving window with the navigation keys (Square + d-pad / Ctrl+Tab + Arrows) are processed in NavUpdateWindowing()
5229
// FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId.
5230
// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
5231
// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
5232
void ImGui::UpdateMouseMovingWindowNewFrame()
5233
{
5234
ImGuiContext& g = *GImGui;
5235
if (g.MovingWindow != NULL)
5236
{
5237
// We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
5238
// We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
5239
KeepAliveID(g.ActiveId);
5240
IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
5241
ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
5242
if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
5243
{
5244
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
5245
SetWindowPos(moving_window, pos, ImGuiCond_Always);
5246
FocusWindow(g.MovingWindow);
5247
}
5248
else
5249
{
5250
StopMouseMovingWindow();
5251
ClearActiveID();
5252
}
5253
}
5254
else
5255
{
5256
// When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
5257
if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
5258
{
5259
KeepAliveID(g.ActiveId);
5260
if (!g.IO.MouseDown[0])
5261
ClearActiveID();
5262
}
5263
}
5264
}
5265
5266
// Initiate focusing and moving window when clicking on empty space or title bar.
5267
// Initiate focusing window when clicking on a disabled item.
5268
// Handle left-click and right-click focus.
5269
void ImGui::UpdateMouseMovingWindowEndFrame()
5270
{
5271
ImGuiContext& g = *GImGui;
5272
if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
5273
return;
5274
5275
// Unless we just made a window/popup appear
5276
if (g.NavWindow && g.NavWindow->Appearing)
5277
return;
5278
5279
ImGuiWindow* hovered_window = g.HoveredWindow;
5280
5281
// Click on empty space to focus window and start moving
5282
// (after we're done with all our widgets)
5283
if (g.IO.MouseClicked[0])
5284
{
5285
// Handle the edge case of a popup being closed while clicking in its empty space.
5286
// If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
5287
ImGuiWindow* hovered_root = hovered_window ? hovered_window->RootWindow : NULL;
5288
const bool is_closed_popup = hovered_root && (hovered_root->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(hovered_root->PopupId, ImGuiPopupFlags_AnyPopupLevel);
5289
5290
if (hovered_window != NULL && !is_closed_popup)
5291
{
5292
StartMouseMovingWindow(hovered_window); //-V595
5293
5294
// FIXME: In principle we might be able to call StopMouseMovingWindow() below.
5295
// Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symetrical, at the later doesn't clear ActiveId.
5296
5297
// Cancel moving if clicked outside of title bar
5298
if ((hovered_window->BgClickFlags & ImGuiWindowBgClickFlags_Move) == 0) // set by io.ConfigWindowsMoveFromTitleBarOnly
5299
if (!(hovered_root->Flags & ImGuiWindowFlags_NoTitleBar))
5300
if (!hovered_root->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
5301
g.MovingWindow = NULL;
5302
5303
// Cancel moving if clicked over an item which was disabled or inhibited by popups
5304
// (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)
5305
if (g.HoveredIdIsDisabled)
5306
{
5307
g.MovingWindow = NULL;
5308
g.ActiveIdDisabledId = g.HoveredId;
5309
}
5310
}
5311
else if (hovered_window == NULL && g.NavWindow != NULL)
5312
{
5313
// Clicking on void disable focus
5314
FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
5315
}
5316
}
5317
5318
// With right mouse button we close popups without changing focus based on where the mouse is aimed
5319
// Instead, focus will be restored to the window under the bottom-most closed popup.
5320
// (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
5321
if (g.IO.MouseClicked[1] && g.HoveredId == 0)
5322
{
5323
// Find the top-most window between HoveredWindow and the top-most Modal Window.
5324
// This is where we can trim the popup stack.
5325
ImGuiWindow* modal = GetTopMostPopupModal();
5326
bool hovered_window_above_modal = hovered_window && (modal == NULL || IsWindowAbove(hovered_window, modal));
5327
ClosePopupsOverWindow(hovered_window_above_modal ? hovered_window : modal, true);
5328
}
5329
}
5330
5331
static bool IsWindowActiveAndVisible(ImGuiWindow* window)
5332
{
5333
return window->Active && !window->Hidden;
5334
}
5335
5336
// 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)
5337
void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)
5338
{
5339
ImGuiContext& g = *GImGui;
5340
ImGuiIO& io = g.IO;
5341
5342
// FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
5343
// by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
5344
g.WindowsBorderHoverPadding = ImMax(ImMax(g.Style.TouchExtraPadding.x, g.Style.TouchExtraPadding.y), g.Style.WindowBorderHoverPadding);
5345
5346
// Find the window hovered by mouse:
5347
// - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
5348
// - 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.
5349
// - 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.
5350
bool clear_hovered_windows = false;
5351
FindHoveredWindowEx(mouse_pos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
5352
g.HoveredWindowBeforeClear = g.HoveredWindow;
5353
5354
// Modal windows prevents mouse from hovering behind them.
5355
ImGuiWindow* modal_window = GetTopMostPopupModal();
5356
if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))
5357
clear_hovered_windows = true;
5358
5359
// Disabled mouse hovering (we don't currently clear MousePos, we could)
5360
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
5361
clear_hovered_windows = true;
5362
5363
// We track click ownership. When clicked outside of a window the click is owned by the application and
5364
// won't report hovering nor request capture even while dragging over our windows afterward.
5365
const bool has_open_popup = (g.OpenPopupStack.Size > 0);
5366
const bool has_open_modal = (modal_window != NULL);
5367
int mouse_earliest_down = -1;
5368
bool mouse_any_down = false;
5369
for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++)
5370
{
5371
if (io.MouseClicked[i])
5372
{
5373
io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
5374
io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
5375
}
5376
mouse_any_down |= io.MouseDown[i];
5377
if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
5378
if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
5379
mouse_earliest_down = i;
5380
}
5381
const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
5382
const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
5383
5384
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
5385
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
5386
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
5387
if (!mouse_avail && !mouse_dragging_extern_payload)
5388
clear_hovered_windows = true;
5389
5390
if (clear_hovered_windows)
5391
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
5392
5393
// Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
5394
// Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
5395
if (g.WantCaptureMouseNextFrame != -1)
5396
{
5397
io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
5398
}
5399
else
5400
{
5401
io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
5402
io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
5403
}
5404
5405
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
5406
io.WantCaptureKeyboard = false;
5407
if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
5408
{
5409
if ((g.ActiveId != 0) || (modal_window != NULL))
5410
io.WantCaptureKeyboard = true;
5411
else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
5412
io.WantCaptureKeyboard = true;
5413
}
5414
if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
5415
io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
5416
5417
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
5418
io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
5419
}
5420
5421
// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
5422
// FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
5423
static void SetupDrawListSharedData()
5424
{
5425
ImGuiContext& g = *GImGui;
5426
ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
5427
for (ImGuiViewportP* viewport : g.Viewports)
5428
virtual_space.Add(viewport->GetMainRect());
5429
g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
5430
g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
5431
g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
5432
g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
5433
if (g.Style.AntiAliasedLines)
5434
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
5435
if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
5436
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
5437
if (g.Style.AntiAliasedFill)
5438
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
5439
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
5440
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
5441
g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments.
5442
}
5443
5444
void ImGui::NewFrame()
5445
{
5446
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5447
ImGuiContext& g = *GImGui;
5448
5449
// Remove pending delete hooks before frame start.
5450
// This deferred removal avoid issues of removal while iterating the hook vector
5451
for (int n = g.Hooks.Size - 1; n >= 0; n--)
5452
if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
5453
g.Hooks.erase(&g.Hooks[n]);
5454
5455
CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
5456
5457
// Check and assert for various common IO and Configuration mistakes
5458
ErrorCheckNewFrameSanityChecks();
5459
5460
// Load settings on first frame, save settings when modified (after a delay)
5461
UpdateSettings();
5462
5463
g.Time += g.IO.DeltaTime;
5464
g.FrameCount += 1;
5465
g.TooltipOverrideCount = 0;
5466
g.WindowsActiveCount = 0;
5467
g.MenusIdSubmittedThisFrame.resize(0);
5468
5469
// Calculate frame-rate for the user, as a purely luxurious feature
5470
g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
5471
g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
5472
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_COUNTOF(g.FramerateSecPerFrame);
5473
g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_COUNTOF(g.FramerateSecPerFrame));
5474
g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
5475
5476
// Process input queue (trickle as many events as possible), turn events into writes to IO structure
5477
g.InputEventsTrail.resize(0);
5478
UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
5479
5480
// Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
5481
UpdateViewportsNewFrame();
5482
5483
// Update texture list (collect destroyed textures, etc.)
5484
UpdateTexturesNewFrame();
5485
5486
// Setup current font and draw list shared data
5487
SetupDrawListSharedData();
5488
UpdateFontsNewFrame();
5489
5490
g.WithinFrameScope = true;
5491
5492
// Mark rendering data as invalid to prevent user who may have a handle on it to use it.
5493
for (ImGuiViewportP* viewport : g.Viewports)
5494
viewport->DrawDataP.Valid = false;
5495
5496
// Drag and drop keep the source ID alive so even if the source disappear our state is consistent
5497
if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
5498
KeepAliveID(g.DragDropPayload.SourceId);
5499
5500
// [DEBUG]
5501
if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding Ctrl
5502
g.DebugDrawIdConflictsId = 0;
5503
if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
5504
g.DebugDrawIdConflictsId = g.HoveredIdPreviousFrame;
5505
5506
// Update HoveredId data
5507
if (!g.HoveredIdPreviousFrame)
5508
g.HoveredIdTimer = 0.0f;
5509
if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
5510
g.HoveredIdNotActiveTimer = 0.0f;
5511
if (g.HoveredId)
5512
g.HoveredIdTimer += g.IO.DeltaTime;
5513
if (g.HoveredId && g.ActiveId != g.HoveredId)
5514
g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
5515
g.HoveredIdPreviousFrame = g.HoveredId;
5516
g.HoveredIdPreviousFrameItemCount = 0;
5517
g.HoveredId = 0;
5518
g.HoveredIdAllowOverlap = false;
5519
g.HoveredIdIsDisabled = false;
5520
g.ScrollbarHeld >>= 1;
5521
5522
// Clear ActiveID if the item is not alive anymore.
5523
// In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
5524
// As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
5525
if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
5526
{
5527
IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
5528
ClearActiveID();
5529
}
5530
5531
// Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
5532
if (g.ActiveId)
5533
g.ActiveIdTimer += g.IO.DeltaTime;
5534
g.LastActiveIdTimer += g.IO.DeltaTime;
5535
g.ActiveIdPreviousFrame = g.ActiveId;
5536
g.ActiveIdIsAlive = 0;
5537
g.ActiveIdHasBeenEditedThisFrame = false;
5538
g.ActiveIdIsJustActivated = false;
5539
if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
5540
g.TempInputId = 0;
5541
if (g.ActiveId == 0)
5542
{
5543
g.ActiveIdUsingNavDirMask = 0x00;
5544
g.ActiveIdUsingAllKeyboardKeys = false;
5545
}
5546
if (g.DeactivatedItemData.ElapseFrame < g.FrameCount)
5547
g.DeactivatedItemData.ID = 0;
5548
g.DeactivatedItemData.IsAlive = false;
5549
5550
// Record when we have been stationary as this state is preserved while over same item.
5551
// FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
5552
// To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
5553
if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5554
g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
5555
else if (g.HoverItemDelayId == 0)
5556
g.HoverItemUnlockedStationaryId = 0;
5557
if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5558
g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
5559
else if (g.HoveredWindow == NULL)
5560
g.HoverWindowUnlockedStationaryId = 0;
5561
5562
// Update hover delay for IsItemHovered() with delays and tooltips
5563
g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
5564
if (g.HoverItemDelayId != 0)
5565
{
5566
g.HoverItemDelayTimer += g.IO.DeltaTime;
5567
g.HoverItemDelayClearTimer = 0.0f;
5568
g.HoverItemDelayId = 0;
5569
}
5570
else if (g.HoverItemDelayTimer > 0.0f)
5571
{
5572
// This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
5573
// We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
5574
g.HoverItemDelayClearTimer += g.IO.DeltaTime;
5575
if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
5576
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.
5577
}
5578
5579
// Close popups on focus lost (currently wip/opt-in)
5580
//if (g.IO.AppFocusLost)
5581
// ClosePopupsExceptModals();
5582
5583
// Update keyboard input state
5584
UpdateKeyboardInputs();
5585
5586
//IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
5587
//IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
5588
//IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
5589
//IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
5590
5591
// Drag and drop
5592
g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
5593
g.DragDropAcceptIdCurr = 0;
5594
g.DragDropAcceptFlagsPrev = g.DragDropAcceptFlagsCurr;
5595
g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None;
5596
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
5597
g.DragDropWithinSource = false;
5598
g.DragDropWithinTarget = false;
5599
g.DragDropHoldJustPressedId = 0;
5600
if (g.DragDropActive)
5601
{
5602
// Also works when g.ActiveId==0 (aka leftover payload in progress, no active id)
5603
// You may disable this externally by hijacking the input route:
5604
// 'if (GetDragDropPayload() != NULL) { Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive); }
5605
// but you will not get a return value from Shortcut() due to ActiveIdUsingAllKeyboardKeys logic. You can however poll IsKeyPressed(ImGuiKey_Escape) afterwards.
5606
ImGuiID owner_id = g.ActiveId ? g.ActiveId : ImHashStr("##DragDropCancelHandler");
5607
if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteGlobal, owner_id))
5608
{
5609
ClearActiveID();
5610
ClearDragDrop();
5611
}
5612
}
5613
g.TooltipPreviousWindow = NULL;
5614
5615
// Update keyboard/gamepad navigation
5616
NavUpdate();
5617
5618
// Update mouse input state
5619
UpdateMouseInputs();
5620
5621
// Mark all windows as not visible and compact unused memory.
5622
IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
5623
const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
5624
for (ImGuiWindow* window : g.Windows)
5625
{
5626
window->WasActive = window->Active;
5627
window->Active = false;
5628
window->WriteAccessed = false;
5629
window->BeginCountPreviousFrame = window->BeginCount;
5630
window->BeginCount = 0;
5631
5632
// Garbage collect transient buffers of recently unused windows
5633
if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
5634
GcCompactTransientWindowBuffers(window);
5635
}
5636
5637
// Find hovered window
5638
// (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
5639
// (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
5640
UpdateHoveredWindowAndCaptureFlags(g.IO.MousePos);
5641
5642
// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
5643
UpdateMouseMovingWindowNewFrame();
5644
5645
// Background darkening/whitening
5646
if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
5647
g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
5648
else
5649
g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
5650
5651
g.MouseCursor = ImGuiMouseCursor_Arrow;
5652
g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
5653
5654
// Platform IME data: reset for the frame
5655
g.PlatformImeDataPrev = g.PlatformImeData;
5656
g.PlatformImeData.WantVisible = g.PlatformImeData.WantTextInput = false;
5657
5658
// Mouse wheel scrolling, scale
5659
UpdateMouseWheel();
5660
5661
// Garbage collect transient buffers of recently unused tables
5662
for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
5663
if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
5664
TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
5665
for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
5666
if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
5667
TableGcCompactTransientBuffers(&table_temp_data);
5668
if (g.GcCompactAll)
5669
GcCompactTransientMiscBuffers();
5670
g.GcCompactAll = false;
5671
5672
// Closing the focused window restore focus to the first active root window in descending z-order
5673
if (g.NavWindow && !g.NavWindow->WasActive)
5674
FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
5675
5676
// No window should be open at the beginning of the frame.
5677
// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
5678
g.CurrentWindowStack.resize(0);
5679
g.BeginPopupStack.resize(0);
5680
g.ItemFlagsStack.resize(0);
5681
g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
5682
g.CurrentItemFlags = g.ItemFlagsStack.back();
5683
g.GroupStack.resize(0);
5684
5685
// [DEBUG] Update debug features
5686
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5687
UpdateDebugToolItemPicker();
5688
UpdateDebugToolItemPathQuery();
5689
UpdateDebugToolFlashStyleColor();
5690
if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
5691
{
5692
g.DebugLocateId = 0;
5693
g.DebugBreakInLocateId = false;
5694
}
5695
if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
5696
{
5697
DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
5698
g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
5699
g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
5700
}
5701
#endif
5702
5703
// Create implicit/fallback window - which we will only render it if the user has added something to it.
5704
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
5705
// This fallback is particularly important as it prevents ImGui:: calls from crashing.
5706
g.WithinFrameScopeWithImplicitWindow = true;
5707
SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
5708
Begin("Debug##Default");
5709
IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
5710
5711
// Store stack sizes
5712
g.ErrorCountCurrentFrame = 0;
5713
ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
5714
5715
// [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
5716
// allowing to validate correct Begin/End behavior in user code.
5717
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5718
if (g.IO.ConfigDebugBeginReturnValueLoop)
5719
g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
5720
else
5721
g.DebugBeginReturnValueCullDepth = -1;
5722
#endif
5723
5724
CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
5725
}
5726
5727
// FIXME: Add a more explicit sort order in the window structure.
5728
static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
5729
{
5730
const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
5731
const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
5732
if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
5733
return d;
5734
if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
5735
return d;
5736
return a->BeginOrderWithinParent - b->BeginOrderWithinParent;
5737
}
5738
5739
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
5740
{
5741
out_sorted_windows->push_back(window);
5742
if (window->Active)
5743
{
5744
int count = window->DC.ChildWindows.Size;
5745
ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
5746
for (int i = 0; i < count; i++)
5747
{
5748
ImGuiWindow* child = window->DC.ChildWindows[i];
5749
if (child->Active)
5750
AddWindowToSortBuffer(out_sorted_windows, child);
5751
}
5752
}
5753
}
5754
5755
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
5756
{
5757
ImGuiContext& g = *GImGui;
5758
ImGuiViewportP* viewport = g.Viewports[0];
5759
g.IO.MetricsRenderWindows++;
5760
if (window->DrawList->_Splitter._Count > 1)
5761
window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
5762
ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
5763
for (ImGuiWindow* child : window->DC.ChildWindows)
5764
if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
5765
AddWindowToDrawData(child, layer);
5766
}
5767
5768
static inline int GetWindowDisplayLayer(ImGuiWindow* window)
5769
{
5770
return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
5771
}
5772
5773
// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
5774
static inline void AddRootWindowToDrawData(ImGuiWindow* window)
5775
{
5776
AddWindowToDrawData(window, GetWindowDisplayLayer(window));
5777
}
5778
5779
static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
5780
{
5781
int n = builder->Layers[0]->Size;
5782
int full_size = n;
5783
for (int i = 1; i < IM_COUNTOF(builder->Layers); i++)
5784
full_size += builder->Layers[i]->Size;
5785
builder->Layers[0]->resize(full_size);
5786
for (int layer_n = 1; layer_n < IM_COUNTOF(builder->Layers); layer_n++)
5787
{
5788
ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
5789
if (layer->empty())
5790
continue;
5791
memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
5792
n += layer->Size;
5793
layer->resize(0);
5794
}
5795
}
5796
5797
static void InitViewportDrawData(ImGuiViewportP* viewport)
5798
{
5799
ImGuiIO& io = ImGui::GetIO();
5800
ImDrawData* draw_data = &viewport->DrawDataP;
5801
5802
viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
5803
viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
5804
viewport->DrawDataBuilder.Layers[0]->resize(0);
5805
viewport->DrawDataBuilder.Layers[1]->resize(0);
5806
5807
draw_data->Valid = true;
5808
draw_data->CmdListsCount = 0;
5809
draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
5810
draw_data->DisplayPos = viewport->Pos;
5811
draw_data->DisplaySize = viewport->Size;
5812
draw_data->FramebufferScale = io.DisplayFramebufferScale;
5813
draw_data->OwnerViewport = viewport;
5814
draw_data->Textures = &ImGui::GetPlatformIO().Textures;
5815
}
5816
5817
// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
5818
// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
5819
// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
5820
// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
5821
// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
5822
// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
5823
// - This is analogous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
5824
// which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
5825
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
5826
{
5827
ImGuiWindow* window = GetCurrentWindow();
5828
window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
5829
window->ClipRect = window->DrawList->_ClipRectStack.back();
5830
}
5831
5832
void ImGui::PopClipRect()
5833
{
5834
ImGuiWindow* window = GetCurrentWindow();
5835
window->DrawList->PopClipRect();
5836
window->ClipRect = window->DrawList->_ClipRectStack.back();
5837
}
5838
5839
static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
5840
{
5841
if ((col & IM_COL32_A_MASK) == 0)
5842
return;
5843
5844
ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport();
5845
ImRect viewport_rect = viewport->GetMainRect();
5846
5847
// Draw behind window by moving the draw command at the FRONT of the draw list
5848
{
5849
// We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows,
5850
// and draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
5851
// 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.
5852
ImDrawList* draw_list = window->RootWindow->DrawList;
5853
if (draw_list->CmdBuffer.Size == 0)
5854
draw_list->AddDrawCmd();
5855
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)
5856
draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
5857
ImDrawCmd cmd = draw_list->CmdBuffer.back();
5858
IM_ASSERT(cmd.ElemCount == 6);
5859
draw_list->CmdBuffer.pop_back();
5860
draw_list->CmdBuffer.push_front(cmd);
5861
draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
5862
draw_list->PopClipRect();
5863
}
5864
}
5865
5866
ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
5867
{
5868
ImGuiContext& g = *GImGui;
5869
ImGuiWindow* bottom_most_visible_window = parent_window;
5870
for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
5871
{
5872
ImGuiWindow* window = g.Windows[i];
5873
if (window->Flags & ImGuiWindowFlags_ChildWindow)
5874
continue;
5875
if (!IsWindowWithinBeginStackOf(window, parent_window))
5876
break;
5877
if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
5878
bottom_most_visible_window = window;
5879
}
5880
return bottom_most_visible_window;
5881
}
5882
5883
static void ImGui::RenderDimmedBackgrounds()
5884
{
5885
ImGuiContext& g = *GImGui;
5886
ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
5887
if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
5888
return;
5889
const bool dim_bg_for_modal = (modal_window != NULL);
5890
const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
5891
if (!dim_bg_for_modal && !dim_bg_for_window_list)
5892
return;
5893
5894
if (dim_bg_for_modal)
5895
{
5896
// Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
5897
ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
5898
RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
5899
}
5900
else if (dim_bg_for_window_list)
5901
{
5902
// Draw dimming behind Ctrl+Tab target window and behind Ctrl+Tab UI window
5903
RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5904
5905
// Draw border around Ctrl+Tab target window
5906
ImGuiWindow* window = g.NavWindowingTargetAnim;
5907
ImGuiViewport* viewport = GetMainViewport();
5908
float distance = g.FontSize;
5909
ImRect bb = window->Rect();
5910
bb.Expand(distance);
5911
if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
5912
bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
5913
if (window->DrawList->CmdBuffer.Size == 0)
5914
window->DrawList->AddDrawCmd();
5915
window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
5916
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI
5917
window->DrawList->PopClipRect();
5918
}
5919
}
5920
5921
// 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.
5922
void ImGui::EndFrame()
5923
{
5924
ImGuiContext& g = *GImGui;
5925
IM_ASSERT(g.Initialized);
5926
5927
// Don't process EndFrame() multiple times.
5928
if (g.FrameCountEnded == g.FrameCount)
5929
return;
5930
if (!g.WithinFrameScope)
5931
{
5932
IM_ASSERT_USER_ERROR(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?");
5933
return;
5934
}
5935
5936
CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
5937
5938
// [EXPERIMENTAL] Recover from errors
5939
if (g.IO.ConfigErrorRecovery)
5940
ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
5941
ErrorCheckEndFrameSanityChecks();
5942
ErrorCheckEndFrameFinalizeErrorTooltip();
5943
5944
// Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
5945
ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
5946
if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
5947
{
5948
IM_ASSERT(ime_data->ViewportId == IMGUI_VIEWPORT_DEFAULT_ID); // master branch
5949
ImGuiViewport* viewport = GetMainViewport();
5950
IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f) for Viewport 0x%08X\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y, viewport->ID);
5951
g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);
5952
}
5953
g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0;
5954
5955
// Hide implicit/fallback "Debug" window if it hasn't been used
5956
g.WithinFrameScopeWithImplicitWindow = false;
5957
if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
5958
g.CurrentWindow->Active = false;
5959
End();
5960
5961
// Update navigation: Ctrl+Tab, wrap-around requests
5962
NavEndFrame();
5963
5964
// Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
5965
if (g.DragDropActive)
5966
{
5967
bool is_delivered = g.DragDropPayload.Delivery;
5968
bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));
5969
if (is_delivered || is_elapsed)
5970
ClearDragDrop();
5971
}
5972
5973
// Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
5974
// If you want to handle source item disappearing: instead of submitting your description tooltip
5975
// in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
5976
// (e.g. end of your item loop, or before EndFrame) by reading payload data.
5977
// In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
5978
if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
5979
{
5980
g.DragDropWithinSource = true;
5981
SetTooltip("...");
5982
g.DragDropWithinSource = false;
5983
}
5984
5985
// End frame
5986
g.WithinFrameScope = false;
5987
g.FrameCountEnded = g.FrameCount;
5988
UpdateFontsEndFrame();
5989
5990
// Initiate moving window + handle left-click and right-click focus
5991
UpdateMouseMovingWindowEndFrame();
5992
5993
// Sort the window list so that all child windows are after their parent
5994
// We cannot do that on FocusWindow() because children may not exist yet
5995
g.WindowsTempSortBuffer.resize(0);
5996
g.WindowsTempSortBuffer.reserve(g.Windows.Size);
5997
for (ImGuiWindow* window : g.Windows)
5998
{
5999
if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
6000
continue;
6001
AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
6002
}
6003
6004
// 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.
6005
IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
6006
g.Windows.swap(g.WindowsTempSortBuffer);
6007
g.IO.MetricsActiveWindows = g.WindowsActiveCount;
6008
6009
UpdateTexturesEndFrame();
6010
6011
// Unlock font atlas
6012
for (ImFontAtlas* atlas : g.FontAtlases)
6013
atlas->Locked = false;
6014
6015
// Clear Input data for next frame
6016
g.IO.MousePosPrev = g.IO.MousePos;
6017
g.IO.AppFocusLost = false;
6018
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
6019
g.IO.InputQueueCharacters.resize(0);
6020
6021
CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
6022
}
6023
6024
// Prepare the data for rendering so you can call GetDrawData()
6025
// (As with anything within the ImGui:: namespace this doesn't touch your GPU or graphics API at all:
6026
// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
6027
void ImGui::Render()
6028
{
6029
ImGuiContext& g = *GImGui;
6030
IM_ASSERT(g.Initialized);
6031
6032
if (g.FrameCountEnded != g.FrameCount)
6033
EndFrame();
6034
if (g.FrameCountRendered == g.FrameCount)
6035
return;
6036
g.FrameCountRendered = g.FrameCount;
6037
6038
g.IO.MetricsRenderWindows = 0;
6039
CallContextHooks(&g, ImGuiContextHookType_RenderPre);
6040
6041
// Add background ImDrawList (for each active viewport)
6042
for (ImGuiViewportP* viewport : g.Viewports)
6043
{
6044
InitViewportDrawData(viewport);
6045
if (viewport->BgFgDrawLists[0] != NULL)
6046
AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
6047
}
6048
6049
// Draw modal/window whitening backgrounds
6050
RenderDimmedBackgrounds();
6051
6052
// Add ImDrawList to render
6053
ImGuiWindow* windows_to_render_top_most[2];
6054
windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
6055
windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
6056
for (ImGuiWindow* window : g.Windows)
6057
{
6058
IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
6059
if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
6060
AddRootWindowToDrawData(window);
6061
}
6062
for (int n = 0; n < IM_COUNTOF(windows_to_render_top_most); n++)
6063
if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
6064
AddRootWindowToDrawData(windows_to_render_top_most[n]);
6065
6066
// Draw software mouse cursor if requested by io.MouseDrawCursor flag
6067
if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
6068
RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
6069
6070
// Setup ImDrawData structures for end-user
6071
g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
6072
for (ImGuiViewportP* viewport : g.Viewports)
6073
{
6074
FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
6075
6076
// Add foreground ImDrawList (for each active viewport)
6077
if (viewport->BgFgDrawLists[1] != NULL)
6078
AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
6079
6080
// We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
6081
ImDrawData* draw_data = &viewport->DrawDataP;
6082
IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
6083
for (ImDrawList* draw_list : draw_data->CmdLists)
6084
draw_list->_PopUnusedDrawCmd();
6085
6086
g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
6087
g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
6088
}
6089
6090
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
6091
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
6092
for (ImFontAtlas* atlas : g.FontAtlases)
6093
ImFontAtlasDebugLogTextureRequests(atlas);
6094
#endif
6095
6096
CallContextHooks(&g, ImGuiContextHookType_RenderPost);
6097
}
6098
6099
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
6100
// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
6101
ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
6102
{
6103
ImGuiContext& g = *GImGui;
6104
6105
const char* text_display_end;
6106
if (hide_text_after_double_hash)
6107
text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
6108
else
6109
text_display_end = text_end;
6110
6111
ImFont* font = g.Font;
6112
const float font_size = g.FontSize;
6113
if (text == text_display_end)
6114
return ImVec2(0.0f, font_size);
6115
ImVec2 text_size = font->CalcTextSizeA(font_size, g.FontWeight, FLT_MAX, wrap_width, text, text_display_end, NULL);
6116
6117
// Round
6118
// FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
6119
// FIXME: Investigate using ceilf or e.g.
6120
// - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
6121
// - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
6122
text_size.x = IM_TRUNC(text_size.x + 0.99999f);
6123
6124
return text_size;
6125
}
6126
6127
// Find window given position, search front-to-back
6128
// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
6129
// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
6130
// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
6131
// called, aka before the next Begin(). Moving window isn't affected.
6132
// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
6133
void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
6134
{
6135
ImGuiContext& g = *GImGui;
6136
ImGuiWindow* hovered_window = NULL;
6137
ImGuiWindow* hovered_window_under_moving_window = NULL;
6138
6139
if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
6140
hovered_window = g.MovingWindow;
6141
6142
ImVec2 padding_regular = g.Style.TouchExtraPadding;
6143
ImVec2 padding_for_resize = ImMax(g.Style.TouchExtraPadding, ImVec2(g.Style.WindowBorderHoverPadding, g.Style.WindowBorderHoverPadding));
6144
for (int i = g.Windows.Size - 1; i >= 0; i--)
6145
{
6146
ImGuiWindow* window = g.Windows[i];
6147
IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6148
if (!window->WasActive || window->Hidden)
6149
continue;
6150
if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
6151
continue;
6152
6153
// Using the clipped AABB, a child window will typically be clipped by its parent (not always)
6154
ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
6155
if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
6156
continue;
6157
6158
// Support for one rectangular hole in any given window
6159
// FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
6160
if (window->HitTestHoleSize.x != 0)
6161
{
6162
ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
6163
ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
6164
if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
6165
continue;
6166
}
6167
6168
if (find_first_and_in_any_viewport)
6169
{
6170
hovered_window = window;
6171
break;
6172
}
6173
else
6174
{
6175
if (hovered_window == NULL)
6176
hovered_window = window;
6177
IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6178
if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
6179
hovered_window_under_moving_window = window;
6180
if (hovered_window && hovered_window_under_moving_window)
6181
break;
6182
}
6183
}
6184
6185
*out_hovered_window = hovered_window;
6186
if (out_hovered_window_under_moving_window != NULL)
6187
*out_hovered_window_under_moving_window = hovered_window_under_moving_window;
6188
}
6189
6190
bool ImGui::IsItemActive()
6191
{
6192
ImGuiContext& g = *GImGui;
6193
if (g.ActiveId)
6194
return g.ActiveId == g.LastItemData.ID;
6195
return false;
6196
}
6197
6198
bool ImGui::IsItemActivated()
6199
{
6200
ImGuiContext& g = *GImGui;
6201
if (g.ActiveId)
6202
if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
6203
return true;
6204
return false;
6205
}
6206
6207
bool ImGui::IsItemDeactivated()
6208
{
6209
ImGuiContext& g = *GImGui;
6210
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
6211
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
6212
return g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount;
6213
}
6214
6215
bool ImGui::IsItemDeactivatedAfterEdit()
6216
{
6217
ImGuiContext& g = *GImGui;
6218
return IsItemDeactivated() && g.DeactivatedItemData.HasBeenEditedBefore;
6219
}
6220
6221
// == (GetItemID() == GetFocusID() && GetFocusID() != 0)
6222
bool ImGui::IsItemFocused()
6223
{
6224
ImGuiContext& g = *GImGui;
6225
return g.NavId == g.LastItemData.ID && g.NavId != 0;
6226
}
6227
6228
// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
6229
// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
6230
bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
6231
{
6232
return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
6233
}
6234
6235
bool ImGui::IsItemToggledOpen()
6236
{
6237
ImGuiContext& g = *GImGui;
6238
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
6239
}
6240
6241
// Call after a Selectable() or TreeNode() involved in multi-selection.
6242
// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
6243
// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
6244
// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
6245
// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
6246
bool ImGui::IsItemToggledSelection()
6247
{
6248
ImGuiContext& g = *GImGui;
6249
IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
6250
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
6251
}
6252
6253
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
6254
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
6255
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
6256
bool ImGui::IsAnyItemHovered()
6257
{
6258
ImGuiContext& g = *GImGui;
6259
return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
6260
}
6261
6262
bool ImGui::IsAnyItemActive()
6263
{
6264
ImGuiContext& g = *GImGui;
6265
return g.ActiveId != 0;
6266
}
6267
6268
bool ImGui::IsAnyItemFocused()
6269
{
6270
ImGuiContext& g = *GImGui;
6271
return g.NavId != 0 && g.NavCursorVisible;
6272
}
6273
6274
bool ImGui::IsItemVisible()
6275
{
6276
ImGuiContext& g = *GImGui;
6277
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
6278
}
6279
6280
bool ImGui::IsItemEdited()
6281
{
6282
ImGuiContext& g = *GImGui;
6283
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
6284
}
6285
6286
// Allow next item to be overlapped by subsequent items.
6287
// This works by requiring HoveredId to match for two subsequent frames,
6288
// so if a following items overwrite it our interactions will naturally be disabled.
6289
void ImGui::SetNextItemAllowOverlap()
6290
{
6291
ImGuiContext& g = *GImGui;
6292
g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
6293
}
6294
6295
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6296
// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
6297
// Use SetNextItemAllowOverlap() *before* your item instead of calling this!
6298
//void ImGui::SetItemAllowOverlap()
6299
//{
6300
// ImGuiContext& g = *GImGui;
6301
// ImGuiID id = g.LastItemData.ID;
6302
// if (g.HoveredId == id)
6303
// g.HoveredIdAllowOverlap = true;
6304
// if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
6305
// g.ActiveIdAllowOverlap = true;
6306
//}
6307
#endif
6308
6309
// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
6310
// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
6311
void ImGui::SetActiveIdUsingAllKeyboardKeys()
6312
{
6313
ImGuiContext& g = *GImGui;
6314
IM_ASSERT(g.ActiveId != 0);
6315
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
6316
g.ActiveIdUsingAllKeyboardKeys = true;
6317
NavMoveRequestCancel();
6318
}
6319
6320
ImGuiID ImGui::GetItemID()
6321
{
6322
ImGuiContext& g = *GImGui;
6323
return g.LastItemData.ID;
6324
}
6325
6326
ImVec2 ImGui::GetItemRectMin()
6327
{
6328
ImGuiContext& g = *GImGui;
6329
return g.LastItemData.Rect.Min;
6330
}
6331
6332
ImVec2 ImGui::GetItemRectMax()
6333
{
6334
ImGuiContext& g = *GImGui;
6335
return g.LastItemData.Rect.Max;
6336
}
6337
6338
ImVec2 ImGui::GetItemRectSize()
6339
{
6340
ImGuiContext& g = *GImGui;
6341
return g.LastItemData.Rect.GetSize();
6342
}
6343
6344
ImGuiItemFlags ImGui::GetItemFlags()
6345
{
6346
ImGuiContext& g = *GImGui;
6347
return g.LastItemData.ItemFlags;
6348
}
6349
6350
// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
6351
// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
6352
bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6353
{
6354
ImGuiID id = GetCurrentWindow()->GetID(str_id);
6355
return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
6356
}
6357
6358
bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6359
{
6360
return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
6361
}
6362
6363
bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6364
{
6365
ImGuiContext& g = *GImGui;
6366
ImGuiWindow* parent_window = g.CurrentWindow;
6367
IM_ASSERT(id != 0);
6368
6369
// Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
6370
const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened | ImGuiChildFlags_NoNavCancel;
6371
IM_UNUSED(ImGuiChildFlags_SupportedMask_);
6372
IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
6373
IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
6374
if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
6375
{
6376
IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6377
IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6378
}
6379
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6380
//if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
6381
// child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
6382
//if (window_flags & ImGuiWindowFlags_NavFlattened)
6383
// child_flags |= ImGuiChildFlags_NavFlattened;
6384
#endif
6385
if (child_flags & ImGuiChildFlags_AutoResizeX)
6386
child_flags &= ~ImGuiChildFlags_ResizeX;
6387
if (child_flags & ImGuiChildFlags_AutoResizeY)
6388
child_flags &= ~ImGuiChildFlags_ResizeY;
6389
6390
// Set window flags
6391
window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
6392
window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
6393
if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
6394
window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
6395
if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6396
window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
6397
6398
// Special framed style
6399
if (child_flags & ImGuiChildFlags_FrameStyle)
6400
{
6401
PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
6402
PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
6403
PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
6404
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
6405
child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;
6406
window_flags |= ImGuiWindowFlags_NoMove;
6407
}
6408
6409
// Forward size
6410
// Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
6411
// (the alternative would to store conditional flags per axis, which is possible but more code)
6412
const ImVec2 size_avail = GetContentRegionAvail();
6413
const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
6414
ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
6415
6416
// A SetNextWindowSize() call always has priority (#8020)
6417
// (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
6418
// FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
6419
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
6420
{
6421
if (g.NextWindowData.SizeVal.x > 0.0f)
6422
{
6423
size.x = g.NextWindowData.SizeVal.x;
6424
child_flags &= ~ImGuiChildFlags_ResizeX;
6425
}
6426
if (g.NextWindowData.SizeVal.y > 0.0f)
6427
{
6428
size.y = g.NextWindowData.SizeVal.y;
6429
child_flags &= ~ImGuiChildFlags_ResizeY;
6430
}
6431
}
6432
SetNextWindowSize(size);
6433
6434
// Forward child flags (we allow prior settings to merge but it'll only work for adding flags)
6435
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags)
6436
g.NextWindowData.ChildFlags |= child_flags;
6437
else
6438
g.NextWindowData.ChildFlags = child_flags;
6439
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags;
6440
6441
// 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.
6442
// 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.
6443
// e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
6444
const char* temp_window_name;
6445
/*if (name && parent_window->IDStack.back() == parent_window->ID)
6446
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
6447
else*/
6448
if (name)
6449
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
6450
else
6451
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
6452
6453
// Set style
6454
const float backup_border_size = g.Style.ChildBorderSize;
6455
if ((child_flags & ImGuiChildFlags_Borders) == 0)
6456
g.Style.ChildBorderSize = 0.0f;
6457
6458
// Begin into window
6459
const bool ret = Begin(temp_window_name, NULL, window_flags);
6460
6461
// Restore style
6462
g.Style.ChildBorderSize = backup_border_size;
6463
if (child_flags & ImGuiChildFlags_FrameStyle)
6464
{
6465
PopStyleVar(3);
6466
PopStyleColor();
6467
}
6468
6469
ImGuiWindow* child_window = g.CurrentWindow;
6470
child_window->ChildId = id;
6471
6472
// Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
6473
// While this is not really documented/defined, it seems that the expected thing to do.
6474
if (child_window->BeginCount == 1)
6475
parent_window->DC.CursorPos = child_window->Pos;
6476
6477
// Process navigation-in immediately so NavInit can run on first frame
6478
// Can enter a child if (A) it has navigable items or (B) it can be scrolled.
6479
const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
6480
if (g.ActiveId == temp_id_for_activation)
6481
ClearActiveID();
6482
if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
6483
{
6484
FocusWindow(child_window);
6485
NavInitWindow(child_window, false);
6486
SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
6487
g.ActiveIdSource = g.NavInputSource;
6488
}
6489
return ret;
6490
}
6491
6492
void ImGui::EndChild()
6493
{
6494
ImGuiContext& g = *GImGui;
6495
ImGuiWindow* child_window = g.CurrentWindow;
6496
6497
const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
6498
IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
6499
6500
g.WithinEndChildID = child_window->ID;
6501
ImVec2 child_size = child_window->Size;
6502
End();
6503
if (child_window->BeginCount == 1)
6504
{
6505
ImGuiWindow* parent_window = g.CurrentWindow;
6506
ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
6507
ItemSize(child_size);
6508
const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
6509
if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
6510
{
6511
ItemAdd(bb, child_window->ChildId);
6512
RenderNavCursor(bb, child_window->ChildId);
6513
6514
// 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)
6515
if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
6516
RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);
6517
}
6518
else
6519
{
6520
// Not navigable into
6521
// - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
6522
// - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
6523
ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
6524
6525
// But when flattened we directly reach items, adjust active layer mask accordingly
6526
if (nav_flattened)
6527
parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
6528
}
6529
if (g.HoveredWindow == child_window)
6530
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
6531
child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags;
6532
//SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd()
6533
}
6534
else
6535
{
6536
SetLastItemDataForChildWindowItem(child_window, child_window->Rect());
6537
}
6538
6539
g.WithinEndChildID = backup_within_end_child_id;
6540
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
6541
}
6542
6543
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
6544
{
6545
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
6546
window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
6547
window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
6548
}
6549
6550
ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
6551
{
6552
ImGuiContext& g = *GImGui;
6553
return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
6554
}
6555
6556
ImGuiWindow* ImGui::FindWindowByName(const char* name)
6557
{
6558
ImGuiID id = ImHashStr(name);
6559
return FindWindowByID(id);
6560
}
6561
6562
static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6563
{
6564
window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y));
6565
if (settings->Size.x > 0 && settings->Size.y > 0)
6566
window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
6567
window->Collapsed = settings->Collapsed;
6568
}
6569
6570
static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6571
{
6572
// Initial window state with e.g. default/arbitrary window position
6573
// Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
6574
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6575
window->Pos = main_viewport->Pos + ImVec2(60, 60);
6576
window->Size = window->SizeFull = ImVec2(0, 0);
6577
window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
6578
6579
if (settings != NULL)
6580
{
6581
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
6582
ApplyWindowSettings(window, settings);
6583
}
6584
window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
6585
6586
if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
6587
{
6588
window->AutoFitFramesX = window->AutoFitFramesY = 2;
6589
window->AutoFitOnlyGrows = false;
6590
}
6591
else
6592
{
6593
if (window->Size.x <= 0.0f)
6594
window->AutoFitFramesX = 2;
6595
if (window->Size.y <= 0.0f)
6596
window->AutoFitFramesY = 2;
6597
window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
6598
}
6599
}
6600
6601
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
6602
{
6603
// Create window the first time
6604
//IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
6605
ImGuiContext& g = *GImGui;
6606
ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
6607
window->Flags = flags;
6608
g.WindowsById.SetVoidPtr(window->ID, window);
6609
6610
ImGuiWindowSettings* settings = NULL;
6611
if (!(flags & ImGuiWindowFlags_NoSavedSettings))
6612
if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
6613
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
6614
6615
InitOrLoadWindowSettings(window, settings);
6616
6617
if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
6618
g.Windows.push_front(window); // Quite slow but rare and only once
6619
else
6620
g.Windows.push_back(window);
6621
6622
return window;
6623
}
6624
6625
static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
6626
{
6627
// We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
6628
// FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
6629
// Perhaps should tend further a neater test for this.
6630
ImGuiContext& g = *GImGui;
6631
ImVec2 size_min;
6632
if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
6633
{
6634
size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE;
6635
size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE;
6636
}
6637
else
6638
{
6639
size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : IMGUI_WINDOW_HARD_MIN_SIZE;
6640
size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : IMGUI_WINDOW_HARD_MIN_SIZE;
6641
}
6642
6643
// Reduce artifacts with very small windows
6644
ImGuiWindow* window_for_height = window;
6645
size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
6646
return size_min;
6647
}
6648
6649
static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
6650
{
6651
ImGuiContext& g = *GImGui;
6652
ImVec2 new_size = size_desired;
6653
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)
6654
{
6655
// See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
6656
ImRect cr = g.NextWindowData.SizeConstraintRect;
6657
new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
6658
new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
6659
if (g.NextWindowData.SizeCallback)
6660
{
6661
ImGuiSizeCallbackData data;
6662
data.UserData = g.NextWindowData.SizeCallbackUserData;
6663
data.Pos = window->Pos;
6664
data.CurrentSize = window->SizeFull;
6665
data.DesiredSize = new_size;
6666
g.NextWindowData.SizeCallback(&data);
6667
new_size = data.DesiredSize;
6668
}
6669
new_size.x = IM_TRUNC(new_size.x);
6670
new_size.y = IM_TRUNC(new_size.y);
6671
}
6672
6673
// Minimum size
6674
ImVec2 size_min = CalcWindowMinSize(window);
6675
return ImMax(new_size, size_min);
6676
}
6677
6678
static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
6679
{
6680
bool preserve_old_content_sizes = false;
6681
if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6682
preserve_old_content_sizes = true;
6683
else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
6684
preserve_old_content_sizes = true;
6685
if (preserve_old_content_sizes)
6686
{
6687
*content_size_current = window->ContentSize;
6688
*content_size_ideal = window->ContentSizeIdeal;
6689
return;
6690
}
6691
6692
content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
6693
content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
6694
content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
6695
content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
6696
}
6697
6698
static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents, int axis_mask)
6699
{
6700
ImGuiContext& g = *GImGui;
6701
ImGuiStyle& style = g.Style;
6702
const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
6703
const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
6704
ImVec2 size_pad = window->WindowPadding * 2.0f;
6705
ImVec2 size_desired;
6706
size_desired[ImGuiAxis_X] = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x;
6707
size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y;
6708
6709
// Determine maximum window size
6710
// Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction
6711
ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6712
6713
if (window->Flags & ImGuiWindowFlags_Tooltip)
6714
{
6715
// Tooltip always resize (up to maximum size)
6716
return ImMin(size_desired, size_max);
6717
}
6718
else
6719
{
6720
ImVec2 size_min = CalcWindowMinSize(window);
6721
ImVec2 size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max);
6722
6723
// When the window cannot fit all contents (either because of constraints, either because screen is too small),
6724
// we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
6725
ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6726
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);
6727
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);
6728
if (will_have_scrollbar_x)
6729
size_auto_fit.y += style.ScrollbarSize;
6730
if (will_have_scrollbar_y)
6731
size_auto_fit.x += style.ScrollbarSize;
6732
return size_auto_fit;
6733
}
6734
}
6735
6736
ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
6737
{
6738
ImVec2 size_contents_current;
6739
ImVec2 size_contents_ideal;
6740
CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
6741
ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal, ~0);
6742
ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6743
return size_final;
6744
}
6745
6746
static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
6747
{
6748
if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
6749
return ImGuiCol_PopupBg;
6750
if (window->Flags & ImGuiWindowFlags_ChildWindow)
6751
return ImGuiCol_ChildBg;
6752
return ImGuiCol_WindowBg;
6753
}
6754
6755
static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target_arg, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
6756
{
6757
ImVec2 corner_target = corner_target_arg;
6758
if (window->Flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
6759
{
6760
ImGuiWindow* parent_window = window->ParentWindow;
6761
ImGuiWindowFlags parent_flags = parent_window->Flags;
6762
ImRect limit_rect = parent_window->InnerRect;
6763
limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));
6764
if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))
6765
corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x);
6766
if (parent_flags & ImGuiWindowFlags_NoScrollbar)
6767
corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y);
6768
}
6769
ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
6770
ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
6771
ImVec2 size_expected = pos_max - pos_min;
6772
ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
6773
*out_pos = pos_min;
6774
if (corner_norm.x == 0.0f)
6775
out_pos->x -= (size_constrained.x - size_expected.x);
6776
if (corner_norm.y == 0.0f)
6777
out_pos->y -= (size_constrained.y - size_expected.y);
6778
*out_size = size_constrained;
6779
}
6780
6781
// Data for resizing from resize grip / corner
6782
struct ImGuiResizeGripDef
6783
{
6784
ImVec2 CornerPosN;
6785
ImVec2 InnerDir;
6786
int AngleMin12, AngleMax12;
6787
};
6788
static const ImGuiResizeGripDef resize_grip_def[4] =
6789
{
6790
{ ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
6791
{ ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
6792
{ ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
6793
{ ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
6794
};
6795
6796
// Data for resizing from borders
6797
struct ImGuiResizeBorderDef
6798
{
6799
ImVec2 InnerDir; // Normal toward inside
6800
ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left)
6801
float OuterAngle; // Angle toward outside
6802
};
6803
static const ImGuiResizeBorderDef resize_border_def[4] =
6804
{
6805
{ ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
6806
{ ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
6807
{ ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
6808
{ ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down
6809
};
6810
6811
static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
6812
{
6813
ImRect rect = window->Rect();
6814
if (thickness == 0.0f)
6815
rect.Max -= ImVec2(1, 1);
6816
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); }
6817
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); }
6818
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); }
6819
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); }
6820
IM_ASSERT(0);
6821
return ImRect();
6822
}
6823
6824
// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
6825
ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
6826
{
6827
IM_ASSERT(n >= 0 && n < 4);
6828
ImGuiID id = window->ID;
6829
id = ImHashStr("#RESIZE", 0, id);
6830
id = ImHashData(&n, sizeof(int), id);
6831
return id;
6832
}
6833
6834
// Borders (Left, Right, Up, Down)
6835
ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
6836
{
6837
IM_ASSERT(dir >= 0 && dir < 4);
6838
int n = (int)dir + 4;
6839
ImGuiID id = window->ID;
6840
id = ImHashStr("#RESIZE", 0, id);
6841
id = ImHashData(&n, sizeof(int), id);
6842
return id;
6843
}
6844
6845
// Handle resize for: Resize Grips, Borders, Gamepad
6846
// Return true when using auto-fit (double-click on resize grip)
6847
static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
6848
{
6849
ImGuiContext& g = *GImGui;
6850
ImGuiWindowFlags flags = window->Flags;
6851
6852
if ((flags & ImGuiWindowFlags_NoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6853
return false;
6854
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && (window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6855
return false;
6856
if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
6857
return false;
6858
6859
int ret_auto_fit_mask = 0x00;
6860
const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6861
const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
6862
const float grip_hover_outer_size = g.WindowsBorderHoverPadding;
6863
6864
ImRect clamp_rect = visibility_rect;
6865
const bool window_move_from_title_bar = !(window->BgClickFlags & ImGuiWindowBgClickFlags_Move) && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
6866
if (window_move_from_title_bar)
6867
clamp_rect.Min.y -= window->TitleBarHeight;
6868
6869
ImVec2 pos_target(FLT_MAX, FLT_MAX);
6870
ImVec2 size_target(FLT_MAX, FLT_MAX);
6871
6872
// Resize grips and borders are on layer 1
6873
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6874
6875
// Manual resize grips
6876
PushID("#RESIZE");
6877
for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6878
{
6879
const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
6880
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
6881
6882
// Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6883
bool hovered, held;
6884
ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
6885
if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
6886
if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
6887
ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
6888
ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
6889
ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6890
//GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
6891
if (hovered || held)
6892
SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
6893
6894
if (held && g.IO.MouseDoubleClicked[0])
6895
{
6896
// Auto-fit when double-clicking
6897
ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, ~0);
6898
size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6899
ret_auto_fit_mask = 0x03; // Both axes
6900
ClearActiveID();
6901
}
6902
else if (held)
6903
{
6904
// Resize from any of the four corners
6905
// We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6906
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);
6907
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);
6908
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
6909
corner_target = ImClamp(corner_target, clamp_min, clamp_max);
6910
CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
6911
}
6912
6913
// Only lower-left grip is visible before hovering/activating
6914
const bool resize_grip_visible = held || hovered || (resize_grip_n == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
6915
if (resize_grip_visible)
6916
resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
6917
}
6918
6919
int resize_border_mask = 0x00;
6920
if (window->Flags & ImGuiWindowFlags_ChildWindow)
6921
resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
6922
else
6923
resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
6924
for (int border_n = 0; border_n < 4; border_n++)
6925
{
6926
if ((resize_border_mask & (1 << border_n)) == 0)
6927
continue;
6928
const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6929
const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
6930
6931
bool hovered, held;
6932
ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, g.WindowsBorderHoverPadding);
6933
ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
6934
ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
6935
ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6936
//GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
6937
if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
6938
hovered = false;
6939
if (hovered || held)
6940
SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
6941
if (held && g.IO.MouseDoubleClicked[0])
6942
{
6943
// Double-clicking bottom or right border auto-fit on this axis
6944
// FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
6945
if (border_n == 1 || border_n == 3) // Right and bottom border
6946
{
6947
ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, 1 << axis);
6948
size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
6949
ret_auto_fit_mask |= (1 << axis);
6950
hovered = held = false; // So border doesn't show highlighted at new position
6951
}
6952
ClearActiveID();
6953
}
6954
else if (held)
6955
{
6956
// Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
6957
// 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.
6958
// FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
6959
const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false));
6960
if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
6961
{
6962
g.WindowResizeBorderExpectedRect = border_rect;
6963
g.WindowResizeRelativeMode = false;
6964
}
6965
if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
6966
g.WindowResizeRelativeMode = true;
6967
6968
const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
6969
const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
6970
const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + g.WindowsBorderHoverPadding; // Match ButtonBehavior() padding above.
6971
6972
// Use absolute mode position
6973
ImVec2 border_target = window->Pos;
6974
border_target[axis] = border_target_abs_mode_for_axis;
6975
6976
// Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
6977
bool ignore_resize = false;
6978
if (g.WindowResizeRelativeMode)
6979
{
6980
//GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
6981
border_target[axis] = border_target_rel_mode_for_axis;
6982
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))
6983
ignore_resize = true;
6984
}
6985
6986
// Clamp, apply
6987
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);
6988
ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
6989
border_target = ImClamp(border_target, clamp_min, clamp_max);
6990
if (!ignore_resize)
6991
CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
6992
}
6993
if (hovered)
6994
*border_hovered = border_n;
6995
if (held)
6996
*border_held = border_n;
6997
}
6998
PopID();
6999
7000
// Restore nav layer
7001
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7002
7003
// Navigation resize (keyboard/gamepad)
7004
// FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
7005
// Not even sure the callback works here.
7006
if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
7007
{
7008
ImVec2 nav_resize_dir;
7009
if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
7010
nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
7011
if (g.NavInputSource == ImGuiInputSource_Gamepad)
7012
nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
7013
if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
7014
{
7015
const float NAV_RESIZE_SPEED = 600.0f;
7016
const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
7017
g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
7018
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
7019
g.NavWindowingToggleLayer = false;
7020
g.NavHighlightItemUnderNav = true;
7021
resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
7022
ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
7023
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
7024
{
7025
// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
7026
size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
7027
g.NavWindowingAccumDeltaSize -= accum_floored;
7028
}
7029
}
7030
}
7031
7032
// Apply back modified position/size to window
7033
const ImVec2 old_pos = window->Pos;
7034
const ImVec2 old_size = window->SizeFull;
7035
if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
7036
window->Size.x = window->SizeFull.x = size_target.x;
7037
if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
7038
window->Size.y = window->SizeFull.y = size_target.y;
7039
if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
7040
window->Pos.x = ImTrunc(pos_target.x);
7041
if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
7042
window->Pos.y = ImTrunc(pos_target.y);
7043
if (old_pos.x != window->Pos.x || old_pos.y != window->Pos.y || old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
7044
MarkIniSettingsDirty(window);
7045
7046
// Recalculate next expected border expected coordinates
7047
if (*border_held != -1)
7048
g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, g.WindowsBorderHoverPadding);
7049
7050
return ret_auto_fit_mask;
7051
}
7052
7053
static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
7054
{
7055
ImVec2 size_for_clamping = window->Size;
7056
if (!(window->BgClickFlags & ImGuiWindowBgClickFlags_Move) && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7057
size_for_clamping.y = window->TitleBarHeight;
7058
window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
7059
}
7060
7061
static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
7062
{
7063
const ImGuiResizeBorderDef& def = resize_border_def[border_n];
7064
const float rounding = window->WindowRounding;
7065
const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
7066
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);
7067
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);
7068
window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
7069
}
7070
7071
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
7072
{
7073
ImGuiContext& g = *GImGui;
7074
const float border_size = window->WindowBorderSize;
7075
const ImU32 border_col = GetColorU32(ImGuiCol_Border);
7076
if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
7077
window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
7078
else if (border_size > 0.0f)
7079
{
7080
if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
7081
RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
7082
if (window->ChildFlags & ImGuiChildFlags_ResizeY)
7083
RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
7084
}
7085
if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
7086
{
7087
const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
7088
const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
7089
RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
7090
}
7091
if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7092
{
7093
float y = window->Pos.y + window->TitleBarHeight - 1;
7094
window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize);
7095
}
7096
}
7097
7098
// Draw background and borders
7099
// Draw and handle scrollbars
7100
void 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)
7101
{
7102
ImGuiContext& g = *GImGui;
7103
ImGuiStyle& style = g.Style;
7104
ImGuiWindowFlags flags = window->Flags;
7105
7106
// Ensure that Scrollbar() doesn't read last frame's SkipItems
7107
IM_ASSERT(window->BeginCount == 0);
7108
window->SkipItems = false;
7109
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7110
7111
// Draw window + handle manual resize
7112
// As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
7113
const float window_rounding = window->WindowRounding;
7114
const float window_border_size = window->WindowBorderSize;
7115
if (window->Collapsed)
7116
{
7117
// Title bar only
7118
const float backup_border_size = style.FrameBorderSize;
7119
g.Style.FrameBorderSize = window->WindowBorderSize;
7120
ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
7121
RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
7122
g.Style.FrameBorderSize = backup_border_size;
7123
}
7124
else
7125
{
7126
// Window background
7127
if (!(flags & ImGuiWindowFlags_NoBackground))
7128
{
7129
ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
7130
bool override_alpha = false;
7131
float alpha = 1.0f;
7132
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasBgAlpha)
7133
{
7134
alpha = g.NextWindowData.BgAlphaVal;
7135
override_alpha = true;
7136
}
7137
if (override_alpha)
7138
bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
7139
window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
7140
}
7141
7142
// Title bar
7143
if (!(flags & ImGuiWindowFlags_NoTitleBar))
7144
{
7145
ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
7146
window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
7147
}
7148
7149
// Menu bar
7150
if (flags & ImGuiWindowFlags_MenuBar)
7151
{
7152
ImRect menu_bar_rect = window->MenuBarRect();
7153
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.
7154
window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
7155
if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
7156
window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
7157
}
7158
7159
// Scrollbars
7160
if (window->ScrollbarX)
7161
Scrollbar(ImGuiAxis_X);
7162
if (window->ScrollbarY)
7163
Scrollbar(ImGuiAxis_Y);
7164
7165
// Render resize grips (after their input handling so we don't have a frame of latency)
7166
if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
7167
{
7168
for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
7169
{
7170
const ImU32 col = resize_grip_col[resize_grip_n];
7171
if ((col & IM_COL32_A_MASK) == 0)
7172
continue;
7173
const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
7174
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
7175
const float border_inner = IM_ROUND(window_border_size * 0.5f);
7176
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner)));
7177
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size)));
7178
window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12);
7179
window->DrawList->PathFillConvex(col);
7180
}
7181
}
7182
7183
// Borders
7184
if (handle_borders_and_resize_grips)
7185
RenderWindowOuterBorders(window);
7186
}
7187
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7188
}
7189
7190
// Render title text, collapse button, close button
7191
void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
7192
{
7193
ImGuiContext& g = *GImGui;
7194
ImGuiStyle& style = g.Style;
7195
ImGuiWindowFlags flags = window->Flags;
7196
7197
const bool has_close_button = (p_open != NULL);
7198
const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
7199
7200
// Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
7201
// 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?
7202
const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
7203
g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
7204
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7205
7206
// Layout buttons
7207
// FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
7208
float pad_l = style.FramePadding.x;
7209
float pad_r = style.FramePadding.x;
7210
float button_sz = g.FontSize;
7211
ImVec2 close_button_pos;
7212
ImVec2 collapse_button_pos;
7213
if (has_close_button)
7214
{
7215
close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7216
pad_r += button_sz + style.ItemInnerSpacing.x;
7217
}
7218
if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
7219
{
7220
collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7221
pad_r += button_sz + style.ItemInnerSpacing.x;
7222
}
7223
if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
7224
{
7225
collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
7226
pad_l += button_sz + style.ItemInnerSpacing.x;
7227
}
7228
7229
// Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
7230
if (has_collapse_button)
7231
if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
7232
window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
7233
7234
// Close button
7235
if (has_close_button)
7236
{
7237
ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
7238
g.CurrentItemFlags |= ImGuiItemFlags_NoFocus;
7239
if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
7240
*p_open = false;
7241
g.CurrentItemFlags = backup_item_flags;
7242
}
7243
7244
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7245
g.CurrentItemFlags = item_flags_backup;
7246
7247
// Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
7248
// FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
7249
const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
7250
const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
7251
7252
// As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
7253
// while uncentered title text will still reach edges correctly.
7254
if (pad_l > style.FramePadding.x)
7255
pad_l += g.Style.ItemInnerSpacing.x;
7256
if (pad_r > style.FramePadding.x)
7257
pad_r += g.Style.ItemInnerSpacing.x;
7258
if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
7259
{
7260
float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
7261
float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
7262
pad_l = ImMax(pad_l, pad_extend * centerness);
7263
pad_r = ImMax(pad_r, pad_extend * centerness);
7264
}
7265
7266
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);
7267
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);
7268
if (flags & ImGuiWindowFlags_UnsavedDocument)
7269
{
7270
ImVec2 marker_pos;
7271
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);
7272
marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
7273
if (marker_pos.x > layout_r.Min.x)
7274
{
7275
RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker));
7276
clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
7277
}
7278
}
7279
//if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7280
//if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7281
RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
7282
}
7283
7284
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
7285
{
7286
window->ParentWindow = parent_window;
7287
window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
7288
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
7289
window->RootWindow = parent_window->RootWindow;
7290
if (parent_window && (flags & ImGuiWindowFlags_Popup))
7291
window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
7292
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)))
7293
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
7294
while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
7295
{
7296
IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
7297
window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
7298
}
7299
}
7300
7301
// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
7302
// This is designed as a toy/test-bed for
7303
void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
7304
{
7305
ImGuiContext& g = *GImGui;
7306
window->SkipRefresh = false;
7307
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
7308
return;
7309
if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
7310
{
7311
// FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
7312
if (window->Appearing) // If currently appearing
7313
return;
7314
if (window->Hidden) // If was hidden (previous frame)
7315
return;
7316
if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)
7317
if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window))
7318
return;
7319
if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)
7320
if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window))
7321
return;
7322
window->DrawList = NULL;
7323
window->SkipRefresh = true;
7324
}
7325
}
7326
7327
static void SetWindowActiveForSkipRefresh(ImGuiWindow* window)
7328
{
7329
window->Active = true;
7330
for (ImGuiWindow* child : window->DC.ChildWindows)
7331
if (!child->Hidden)
7332
{
7333
child->Active = child->SkipRefresh = true;
7334
SetWindowActiveForSkipRefresh(child);
7335
}
7336
}
7337
7338
// Push a new Dear ImGui window to add widgets to.
7339
// - 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.
7340
// - Begin/End can be called multiple times during the frame with the same window name to append content.
7341
// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
7342
// 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.
7343
// - 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.
7344
// - 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.
7345
bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
7346
{
7347
ImGuiContext& g = *GImGui;
7348
const ImGuiStyle& style = g.Style;
7349
IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
7350
IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
7351
IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
7352
7353
// Find or create
7354
ImGuiWindow* window = FindWindowByName(name);
7355
const bool window_just_created = (window == NULL);
7356
if (window_just_created)
7357
window = CreateNewWindow(name, flags);
7358
7359
// [DEBUG] Debug break requested by user
7360
if (g.DebugBreakInWindow == window->ID)
7361
IM_DEBUG_BREAK();
7362
7363
// Automatically disable manual moving/resizing when NoInputs is set
7364
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
7365
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
7366
7367
const int current_frame = g.FrameCount;
7368
const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
7369
window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
7370
7371
// Update the Appearing flag
7372
bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
7373
if (flags & ImGuiWindowFlags_Popup)
7374
{
7375
ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7376
window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
7377
window_just_activated_by_user |= (window != popup_ref.Window);
7378
}
7379
window->Appearing = window_just_activated_by_user;
7380
if (window->Appearing)
7381
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
7382
7383
// Update Flags, LastFrameActive, BeginOrderXXX fields
7384
if (first_begin_of_the_frame)
7385
{
7386
UpdateWindowInFocusOrderList(window, window_just_created, flags);
7387
window->Flags = (ImGuiWindowFlags)flags;
7388
window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
7389
window->LastFrameActive = current_frame;
7390
window->LastTimeActive = (float)g.Time;
7391
window->BeginOrderWithinParent = 0;
7392
window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
7393
}
7394
else
7395
{
7396
flags = window->Flags;
7397
}
7398
7399
// 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
7400
ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
7401
ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow;
7402
IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
7403
7404
// We allow window memory to be compacted so recreate the base stack when needed.
7405
if (window->IDStack.Size == 0)
7406
window->IDStack.push_back(window->ID);
7407
7408
// Add to stack
7409
g.CurrentWindow = window;
7410
g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
7411
ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
7412
window_stack_data.Window = window;
7413
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
7414
window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
7415
window_stack_data.DisabledOverrideReenableAlphaBackup = 0.0f;
7416
ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
7417
g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
7418
if (flags & ImGuiWindowFlags_ChildMenu)
7419
g.BeginMenuDepth++;
7420
7421
// Update ->RootWindow and others pointers (before any possible call to FocusWindow)
7422
if (first_begin_of_the_frame)
7423
{
7424
UpdateWindowParentAndRootLinks(window, flags, parent_window);
7425
window->ParentWindowInBeginStack = parent_window_in_stack;
7426
7427
// There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
7428
// e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
7429
window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL;
7430
7431
// Inherent SetWindowFontScale() from parent until we fix this system...
7432
window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f;
7433
}
7434
7435
// Add to focus scope stack
7436
PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
7437
window->NavRootFocusScopeId = g.CurrentFocusScopeId;
7438
7439
// Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
7440
if (flags & ImGuiWindowFlags_Popup)
7441
{
7442
ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7443
popup_ref.Window = window;
7444
popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
7445
g.BeginPopupStack.push_back(popup_ref);
7446
window->PopupId = popup_ref.PopupId;
7447
}
7448
7449
// Process SetNextWindow***() calls
7450
// (FIXME: Consider splitting the HasXXX flags into X/Y components)
7451
bool window_pos_set_by_api = false;
7452
bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
7453
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos)
7454
{
7455
window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
7456
if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
7457
{
7458
// May be processed on the next frame if this is our first frame and we are measuring size
7459
// FIXME: Look into removing the branch so everything can go through this same code path for consistency.
7460
window->SetWindowPosVal = g.NextWindowData.PosVal;
7461
window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
7462
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7463
}
7464
else
7465
{
7466
SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
7467
}
7468
}
7469
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize)
7470
{
7471
window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
7472
window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
7473
if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
7474
g.NextWindowData.SizeVal.x = window->SizeFull.x;
7475
if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
7476
g.NextWindowData.SizeVal.y = window->SizeFull.y;
7477
SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
7478
}
7479
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll)
7480
{
7481
if (g.NextWindowData.ScrollVal.x >= 0.0f)
7482
{
7483
window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
7484
window->ScrollTargetCenterRatio.x = 0.0f;
7485
}
7486
if (g.NextWindowData.ScrollVal.y >= 0.0f)
7487
{
7488
window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
7489
window->ScrollTargetCenterRatio.y = 0.0f;
7490
}
7491
}
7492
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasContentSize)
7493
window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
7494
else if (first_begin_of_the_frame)
7495
window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
7496
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasCollapsed)
7497
SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
7498
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasFocus)
7499
FocusWindow(window);
7500
if (window->Appearing)
7501
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
7502
7503
// [EXPERIMENTAL] Skip Refresh mode
7504
UpdateWindowSkipRefresh(window);
7505
7506
// Nested root windows (typically tooltips) override disabled state
7507
if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
7508
BeginDisabledOverrideReenable();
7509
7510
// We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
7511
g.CurrentWindow = NULL;
7512
7513
// When reusing window again multiple times a frame, just append content (don't need to setup again)
7514
if (first_begin_of_the_frame && !window->SkipRefresh)
7515
{
7516
// Initialize
7517
const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
7518
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
7519
window->Active = true;
7520
window->HasCloseButton = (p_open != NULL);
7521
window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
7522
window->IDStack.resize(1);
7523
window->DrawList->_ResetForNewFrame();
7524
window->DC.CurrentTableIdx = -1;
7525
7526
// Restore buffer capacity when woken from a compacted state, to avoid
7527
if (window->MemoryCompacted)
7528
GcAwakeTransientWindowBuffers(window);
7529
7530
// Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
7531
// 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.
7532
bool window_title_visible_elsewhere = false;
7533
if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->WasActive && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using Ctrl+Tab
7534
window_title_visible_elsewhere = true;
7535
if (flags & ImGuiWindowFlags_ChildMenu)
7536
window_title_visible_elsewhere = true;
7537
if ((window_title_visible_elsewhere || window_just_activated_by_user) && !window_just_created && strcmp(name, window->Name) != 0)
7538
{
7539
size_t buf_len = (size_t)window->NameBufLen;
7540
window->Name = ImStrdupcpy(window->Name, &buf_len, name);
7541
window->NameBufLen = (int)buf_len;
7542
}
7543
7544
// UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
7545
7546
// Update contents size from last frame for auto-fitting (or use explicit size)
7547
CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
7548
if (window->HiddenFramesCanSkipItems > 0)
7549
window->HiddenFramesCanSkipItems--;
7550
if (window->HiddenFramesCannotSkipItems > 0)
7551
window->HiddenFramesCannotSkipItems--;
7552
if (window->HiddenFramesForRenderOnly > 0)
7553
window->HiddenFramesForRenderOnly--;
7554
7555
// Hide new windows for one frame until they calculate their size
7556
if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
7557
window->HiddenFramesCannotSkipItems = 1;
7558
7559
// Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
7560
// We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
7561
if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
7562
{
7563
window->HiddenFramesCannotSkipItems = 1;
7564
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
7565
{
7566
if (!window_size_x_set_by_api)
7567
window->Size.x = window->SizeFull.x = 0.f;
7568
if (!window_size_y_set_by_api)
7569
window->Size.y = window->SizeFull.y = 0.f;
7570
window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
7571
}
7572
}
7573
7574
// SELECT VIEWPORT
7575
// FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
7576
7577
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
7578
SetWindowViewport(window, viewport);
7579
SetCurrentWindow(window);
7580
7581
// LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
7582
7583
if (flags & ImGuiWindowFlags_ChildWindow)
7584
window->WindowBorderSize = style.ChildBorderSize;
7585
else
7586
window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
7587
window->WindowPadding = style.WindowPadding;
7588
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
7589
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
7590
7591
// Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
7592
window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
7593
window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
7594
window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
7595
window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
7596
window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window.
7597
window->ScrollStepSize = style.ScrollStepSize;
7598
7599
// Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
7600
// Those flags will be altered further down in the function depending on more conditions.
7601
bool use_current_size_for_scrollbar_x = window_just_created;
7602
bool use_current_size_for_scrollbar_y = window_just_created;
7603
if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
7604
use_current_size_for_scrollbar_x = true;
7605
if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
7606
use_current_size_for_scrollbar_y = true;
7607
7608
// Collapse window by double-clicking on title bar
7609
// 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
7610
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
7611
{
7612
// We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
7613
// so verify that we don't have items over the title bar.
7614
ImRect title_bar_rect = window->TitleBarRect();
7615
if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
7616
if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
7617
window->WantCollapseToggle = true;
7618
if (window->WantCollapseToggle)
7619
{
7620
window->Collapsed = !window->Collapsed;
7621
if (!window->Collapsed)
7622
use_current_size_for_scrollbar_y = true;
7623
MarkIniSettingsDirty(window);
7624
}
7625
}
7626
else
7627
{
7628
window->Collapsed = false;
7629
}
7630
window->WantCollapseToggle = false;
7631
7632
// SIZE
7633
7634
// Outer Decoration Sizes
7635
// (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
7636
const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
7637
window->DecoOuterSizeX1 = 0.0f;
7638
window->DecoOuterSizeX2 = 0.0f;
7639
window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
7640
window->DecoOuterSizeY2 = 0.0f;
7641
window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
7642
7643
// Calculate auto-fit size, handle automatic resize
7644
// - Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
7645
// - We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
7646
// - Auto-fit may only grow window during the first few frames.
7647
{
7648
const bool size_auto_fit_x_always = !window_size_x_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed;
7649
const bool size_auto_fit_y_always = !window_size_y_set_by_api && (flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed;
7650
const bool size_auto_fit_x_current = !window_size_x_set_by_api && (window->AutoFitFramesX > 0);
7651
const bool size_auto_fit_y_current = !window_size_y_set_by_api && (window->AutoFitFramesY > 0);
7652
int size_auto_fit_mask = 0;
7653
if (size_auto_fit_x_always || size_auto_fit_x_current)
7654
size_auto_fit_mask |= (1 << ImGuiAxis_X);
7655
if (size_auto_fit_y_always || size_auto_fit_y_current)
7656
size_auto_fit_mask |= (1 << ImGuiAxis_Y);
7657
const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal, size_auto_fit_mask);
7658
7659
const ImVec2 old_size = window->SizeFull;
7660
if (size_auto_fit_x_always || size_auto_fit_x_current)
7661
{
7662
if (size_auto_fit_x_always)
7663
window->SizeFull.x = size_auto_fit.x;
7664
else
7665
window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
7666
use_current_size_for_scrollbar_x = true;
7667
}
7668
if (size_auto_fit_y_always || size_auto_fit_y_current)
7669
{
7670
if (size_auto_fit_y_always)
7671
window->SizeFull.y = size_auto_fit.y;
7672
else
7673
window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
7674
use_current_size_for_scrollbar_y = true;
7675
}
7676
if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
7677
MarkIniSettingsDirty(window);
7678
}
7679
7680
// Apply minimum/maximum window size constraints and final size
7681
window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
7682
window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
7683
7684
// POSITION
7685
7686
// Popup latch its initial position, will position itself when it appears next frame
7687
if (window_just_activated_by_user)
7688
{
7689
window->AutoPosLastDirection = ImGuiDir_None;
7690
if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
7691
window->Pos = g.BeginPopupStack.back().OpenPopupPos;
7692
}
7693
7694
// Position child window
7695
if (flags & ImGuiWindowFlags_ChildWindow)
7696
{
7697
IM_ASSERT(parent_window && parent_window->Active);
7698
window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
7699
parent_window->DC.ChildWindows.push_back(window);
7700
if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
7701
window->Pos = parent_window->DC.CursorPos;
7702
}
7703
7704
const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
7705
if (window_pos_with_pivot)
7706
SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
7707
else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
7708
window->Pos = FindBestWindowPosForPopup(window);
7709
else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
7710
window->Pos = FindBestWindowPosForPopup(window);
7711
else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
7712
window->Pos = FindBestWindowPosForPopup(window);
7713
7714
// Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
7715
// When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
7716
ImRect viewport_rect(viewport->GetMainRect());
7717
ImRect viewport_work_rect(viewport->GetWorkRect());
7718
ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
7719
ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
7720
7721
// Clamp position/size so window stays visible within its viewport or monitor
7722
// Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
7723
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
7724
if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
7725
ClampWindowPos(window, visibility_rect);
7726
window->Pos = ImTrunc(window->Pos);
7727
7728
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
7729
// Large values tend to lead to variety of artifacts and are not recommended.
7730
window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
7731
7732
// For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
7733
//if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7734
// window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
7735
7736
// Apply window focus (new and reactivated windows are moved to front)
7737
bool want_focus = false;
7738
if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
7739
{
7740
if (flags & ImGuiWindowFlags_Popup)
7741
want_focus = true;
7742
else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
7743
want_focus = true;
7744
}
7745
7746
// [Test Engine] Register whole window in the item system (before submitting further decorations)
7747
#ifdef IMGUI_ENABLE_TEST_ENGINE
7748
if (g.TestEngineHookItems)
7749
{
7750
IM_ASSERT(window->IDStack.Size == 1);
7751
window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
7752
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7753
IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
7754
IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
7755
window->IDStack.Size = 1;
7756
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7757
7758
}
7759
#endif
7760
7761
// Decide if we are going to handle borders and resize grips
7762
// 'window->SkipItems' is not updated yet so for child windows we rely on ParentWindow to avoid submitting decorations. (#8815)
7763
// Whenever we add support for full decorated child windows we will likely make this logic more general.
7764
bool handle_borders_and_resize_grips = true;
7765
if ((flags & ImGuiWindowFlags_ChildWindow) && window->ParentWindow->SkipItems)
7766
handle_borders_and_resize_grips = false;
7767
7768
// Handle manual resize: Resize Grips, Borders, Gamepad
7769
// Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set.
7770
int border_hovered = -1, border_held = -1;
7771
ImU32 resize_grip_col[4] = {};
7772
int resize_grip_count;
7773
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
7774
resize_grip_count = ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) ? 1 : 0;
7775
else
7776
resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
7777
7778
const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
7779
if (handle_borders_and_resize_grips && !window->Collapsed)
7780
if (int auto_fit_mask = UpdateWindowManualResize(window, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
7781
{
7782
if (auto_fit_mask & (1 << ImGuiAxis_X))
7783
use_current_size_for_scrollbar_x = true;
7784
if (auto_fit_mask & (1 << ImGuiAxis_Y))
7785
use_current_size_for_scrollbar_y = true;
7786
}
7787
window->ResizeBorderHovered = (signed char)border_hovered;
7788
window->ResizeBorderHeld = (signed char)border_held;
7789
7790
// SCROLLBAR VISIBILITY
7791
7792
// Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
7793
if (!window->Collapsed)
7794
{
7795
// When reading the current size we need to read it after size constraints have been applied.
7796
// Intentionally use previous frame values for InnerRect and ScrollbarSizes.
7797
// And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
7798
ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
7799
ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
7800
ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
7801
float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
7802
float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
7803
bool scrollbar_x_prev = window->ScrollbarX;
7804
//bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
7805
window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
7806
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));
7807
7808
// Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488)
7809
// (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause.
7810
// The better solution is to, either (1) enforce visibility by using ImGuiWindowFlags_AlwaysHorizontalScrollbar or (2) declare stable contents width with SetNextWindowContentSize(), if you can compute it)
7811
window->ScrollbarXStabilizeToggledHistory <<= 1;
7812
window->ScrollbarXStabilizeToggledHistory |= (scrollbar_x_prev != window->ScrollbarX) ? 0x01 : 0x00;
7813
const bool scrollbar_x_stabilize = (window->ScrollbarXStabilizeToggledHistory != 0) && ImCountSetBits(window->ScrollbarXStabilizeToggledHistory) >= 4; // 4 == half of bits in our U8 history.
7814
if (scrollbar_x_stabilize)
7815
window->ScrollbarX = true;
7816
//if (scrollbar_x_stabilize && !window->ScrollbarXStabilizeEnabled)
7817
// IMGUI_DEBUG_LOG("[scroll] Stabilize ScrollbarX for Window '%s'\n", window->Name);
7818
window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize;
7819
7820
if (window->ScrollbarX && !window->ScrollbarY)
7821
window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
7822
window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
7823
7824
// Amend the partially filled window->DecorationXXX values.
7825
window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
7826
window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
7827
}
7828
7829
// UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
7830
// Update various regions. Variables they depend on should be set above in this function.
7831
// We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
7832
7833
// Outer rectangle
7834
// Not affected by window border size. Used by:
7835
// - FindHoveredWindow() (w/ extra padding when border resize is enabled)
7836
// - Begin() initial clipping rect for drawing window background and borders.
7837
// - Begin() clipping whole child
7838
const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
7839
const ImRect outer_rect = window->Rect();
7840
const ImRect title_bar_rect = window->TitleBarRect();
7841
window->OuterRectClipped = outer_rect;
7842
window->OuterRectClipped.ClipWith(host_rect);
7843
7844
// Inner rectangle
7845
// Not affected by window border size. Used by:
7846
// - InnerClipRect
7847
// - ScrollToRectEx()
7848
// - NavUpdatePageUpPageDown()
7849
// - Scrollbar()
7850
window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
7851
window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
7852
window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
7853
window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
7854
7855
// Inner clipping rectangle.
7856
// - Extend a outside of normal work region up to borders.
7857
// - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
7858
// - It also makes clipped items be more noticeable.
7859
// - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
7860
// - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
7861
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
7862
// Affected by window/frame border size. Used by:
7863
// - Begin() initial clip rect
7864
float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
7865
7866
// Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
7867
// This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,
7868
// see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here).
7869
window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);
7870
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);
7871
window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);
7872
window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);
7873
window->InnerClipRect.ClipWithFull(host_rect);
7874
7875
// SCROLLING
7876
7877
// Lock down maximum scrolling
7878
// The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
7879
// for right/bottom aligned items without creating a scrollbar.
7880
window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
7881
window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
7882
7883
// Apply scrolling
7884
window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7885
window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
7886
window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
7887
7888
// DRAWING
7889
7890
// Setup draw list and outer clipping rectangle
7891
IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
7892
window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef);
7893
PushClipRect(host_rect.Min, host_rect.Max, false);
7894
7895
// Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
7896
// When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
7897
// 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)
7898
{
7899
bool render_decorations_in_parent = false;
7900
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
7901
{
7902
// - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
7903
// - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
7904
ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
7905
bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
7906
bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
7907
if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
7908
render_decorations_in_parent = true;
7909
}
7910
if (render_decorations_in_parent)
7911
window->DrawList = parent_window->DrawList;
7912
7913
// Handle title bar, scrollbar, resize grips and resize borders
7914
const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
7915
const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
7916
RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
7917
7918
if (render_decorations_in_parent)
7919
window->DrawList = &window->DrawListInst;
7920
}
7921
7922
// UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
7923
7924
// Work rectangle.
7925
// Affected by window padding and border size. Used by:
7926
// - Columns() for right-most edge
7927
// - TreeNode(), CollapsingHeader() for right-most edge
7928
// - BeginTabBar() for right-most edge
7929
const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
7930
const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
7931
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)));
7932
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)));
7933
window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
7934
window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
7935
window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
7936
window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
7937
window->ParentWorkRect = window->WorkRect;
7938
7939
// [LEGACY] Content Region
7940
// FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
7941
// Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
7942
// Used by:
7943
// - Mouse wheel scrolling + many other things
7944
window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
7945
window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
7946
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)));
7947
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)));
7948
7949
// Setup drawing context
7950
// (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.)
7951
window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
7952
window->DC.GroupOffset.x = 0.0f;
7953
window->DC.ColumnsOffset.x = 0.0f;
7954
7955
// Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
7956
// 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.
7957
double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
7958
double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
7959
window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
7960
window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
7961
window->DC.CursorPos = window->DC.CursorStartPos;
7962
window->DC.CursorPosPrevLine = window->DC.CursorPos;
7963
window->DC.CursorMaxPos = window->DC.CursorStartPos;
7964
window->DC.IdealMaxPos = window->DC.CursorStartPos;
7965
window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
7966
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
7967
window->DC.IsSameLine = window->DC.IsSetPos = false;
7968
7969
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7970
window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
7971
window->DC.NavLayersActiveMaskNext = 0x00;
7972
window->DC.NavIsScrollPushableX = true;
7973
window->DC.NavHideHighlightOneFrame = false;
7974
window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
7975
7976
window->DC.MenuBarAppending = false;
7977
window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
7978
window->DC.TreeDepth = 0;
7979
window->DC.TreeHasStackDataDepthMask = window->DC.TreeRecordsClippedNodesY2Mask = 0x00;
7980
window->DC.ChildWindows.resize(0);
7981
window->DC.StateStorage = &window->StateStorage;
7982
window->DC.CurrentColumns = NULL;
7983
window->DC.LayoutType = ImGuiLayoutType_Vertical;
7984
window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
7985
7986
// Default item width. Make it proportional to window size if window manually resizes
7987
if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
7988
window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
7989
else
7990
window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
7991
window->DC.ItemWidth = window->ItemWidthDefault;
7992
window->DC.TextWrapPos = -1.0f; // disabled
7993
window->DC.ItemWidthStack.resize(0);
7994
window->DC.TextWrapPosStack.resize(0);
7995
if (flags & ImGuiWindowFlags_Modal)
7996
window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
7997
7998
if (window->AutoFitFramesX > 0)
7999
window->AutoFitFramesX--;
8000
if (window->AutoFitFramesY > 0)
8001
window->AutoFitFramesY--;
8002
8003
// Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
8004
// We ImGuiFocusRequestFlags_UnlessBelowModal to:
8005
// - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
8006
// - Position window behind the modal that is not a begin-parent of this window.
8007
if (want_focus)
8008
FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
8009
if (want_focus && window == g.NavWindow)
8010
NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
8011
8012
// Pressing Ctrl+C copy window content into the clipboard
8013
// [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.
8014
// [EXPERIMENTAL] Text outputs has many issues.
8015
if (g.IO.ConfigWindowsCopyContentsWithCtrlC)
8016
if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C))
8017
LogToClipboard(0);
8018
8019
// Title bar
8020
if (!(flags & ImGuiWindowFlags_NoTitleBar))
8021
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);
8022
8023
// Clear hit test shape every frame
8024
window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
8025
8026
if (flags & ImGuiWindowFlags_Tooltip)
8027
g.TooltipPreviousWindow = window;
8028
8029
// Set default BgClickFlags
8030
// This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overriden by user code.
8031
// FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button.
8032
window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move);
8033
8034
// We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
8035
// This is useful to allow creating context menus on title bar only, etc.
8036
window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None;
8037
window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
8038
SetLastItemDataForWindow(window, title_bar_rect);
8039
8040
// [DEBUG]
8041
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8042
if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
8043
DebugLocateItemResolveWithLastItem();
8044
#endif
8045
8046
// [Test Engine] Register title bar / tab with MoveId.
8047
#ifdef IMGUI_ENABLE_TEST_ENGINE
8048
if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
8049
{
8050
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
8051
IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
8052
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
8053
}
8054
#endif
8055
}
8056
else
8057
{
8058
// Skip refresh always mark active
8059
if (window->SkipRefresh)
8060
SetWindowActiveForSkipRefresh(window);
8061
8062
// Append
8063
SetCurrentWindow(window);
8064
SetLastItemDataForWindow(window, window->TitleBarRect());
8065
}
8066
8067
if (!window->SkipRefresh)
8068
PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
8069
8070
// 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)
8071
window->WriteAccessed = false;
8072
window->BeginCount++;
8073
g.NextWindowData.ClearFlags();
8074
8075
// Update visibility
8076
if (first_begin_of_the_frame && !window->SkipRefresh)
8077
{
8078
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
8079
{
8080
// Child window can be out of sight and have "negative" clip windows.
8081
// Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
8082
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
8083
const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
8084
if (!g.LogEnabled && !nav_request)
8085
if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
8086
{
8087
if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
8088
window->HiddenFramesCannotSkipItems = 1;
8089
else
8090
window->HiddenFramesCanSkipItems = 1;
8091
}
8092
8093
// Hide along with parent or if parent is collapsed
8094
if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
8095
window->HiddenFramesCanSkipItems = 1;
8096
if (parent_window && parent_window->HiddenFramesCannotSkipItems > 0)
8097
window->HiddenFramesCannotSkipItems = 1;
8098
}
8099
8100
// 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)
8101
if (style.Alpha <= 0.0f)
8102
window->HiddenFramesCanSkipItems = 1;
8103
8104
// Update the Hidden flag
8105
bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
8106
window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
8107
8108
// Disable inputs for requested number of frames
8109
if (window->DisableInputsFrames > 0)
8110
{
8111
window->DisableInputsFrames--;
8112
window->Flags |= ImGuiWindowFlags_NoInputs;
8113
}
8114
8115
// Update the SkipItems flag, used to early out of all items functions (no layout required)
8116
bool skip_items = false;
8117
if (window->Collapsed || !window->Active || hidden_regular)
8118
if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
8119
skip_items = true;
8120
window->SkipItems = skip_items;
8121
}
8122
else if (first_begin_of_the_frame)
8123
{
8124
// Skip refresh mode
8125
window->SkipItems = true;
8126
}
8127
8128
// [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
8129
// (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
8130
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8131
if (!window->IsFallbackWindow)
8132
if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
8133
{
8134
if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
8135
if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
8136
return false;
8137
}
8138
#endif
8139
8140
return !window->SkipItems;
8141
}
8142
8143
void ImGui::End()
8144
{
8145
ImGuiContext& g = *GImGui;
8146
ImGuiWindow* window = g.CurrentWindow;
8147
8148
// Error checking: verify that user hasn't called End() too many times!
8149
if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
8150
{
8151
IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
8152
return;
8153
}
8154
ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
8155
8156
// Error checking: verify that user doesn't directly call End() on a child window.
8157
if (window->Flags & ImGuiWindowFlags_ChildWindow)
8158
IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!");
8159
8160
// Close anything that is open
8161
if (window->DC.CurrentColumns)
8162
EndColumns();
8163
if (!window->SkipRefresh)
8164
PopClipRect(); // Inner window clip rectangle
8165
PopFocusScope();
8166
if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
8167
EndDisabledOverrideReenable();
8168
8169
if (window->SkipRefresh)
8170
{
8171
IM_ASSERT(window->DrawList == NULL);
8172
window->DrawList = &window->DrawListInst;
8173
}
8174
8175
// Stop logging
8176
if (g.LogWindow == window) // FIXME: add more options for scope of logging
8177
LogFinish();
8178
8179
if (window->DC.IsSetPos)
8180
ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
8181
8182
// Pop from window stack
8183
g.LastItemData = window_stack_data.ParentLastItemDataBackup;
8184
if (window->Flags & ImGuiWindowFlags_ChildMenu)
8185
g.BeginMenuDepth--;
8186
if (window->Flags & ImGuiWindowFlags_Popup)
8187
g.BeginPopupStack.pop_back();
8188
8189
// Error handling, state recovery
8190
if (g.IO.ConfigErrorRecovery)
8191
ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
8192
8193
g.CurrentWindowStack.pop_back();
8194
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
8195
}
8196
8197
void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
8198
{
8199
ImGuiContext& g = *GImGui;
8200
ImGuiItemFlags item_flags = g.CurrentItemFlags;
8201
IM_ASSERT(item_flags == g.ItemFlagsStack.back());
8202
if (enabled)
8203
item_flags |= option;
8204
else
8205
item_flags &= ~option;
8206
g.CurrentItemFlags = item_flags;
8207
g.ItemFlagsStack.push_back(item_flags);
8208
}
8209
8210
void ImGui::PopItemFlag()
8211
{
8212
ImGuiContext& g = *GImGui;
8213
if (g.ItemFlagsStack.Size <= 1)
8214
{
8215
IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
8216
return;
8217
}
8218
g.ItemFlagsStack.pop_back();
8219
g.CurrentItemFlags = g.ItemFlagsStack.back();
8220
}
8221
8222
// BeginDisabled()/EndDisabled()
8223
// - 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)
8224
// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
8225
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
8226
// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
8227
// (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
8228
// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
8229
void ImGui::BeginDisabled(bool disabled)
8230
{
8231
ImGuiContext& g = *GImGui;
8232
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8233
if (!was_disabled && disabled)
8234
{
8235
g.DisabledAlphaBackup = g.Style.Alpha;
8236
g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
8237
}
8238
if (was_disabled || disabled)
8239
g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
8240
g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
8241
g.DisabledStackSize++;
8242
}
8243
8244
void ImGui::EndDisabled()
8245
{
8246
ImGuiContext& g = *GImGui;
8247
if (g.DisabledStackSize <= 0)
8248
{
8249
IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
8250
return;
8251
}
8252
g.DisabledStackSize--;
8253
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8254
//PopItemFlag();
8255
g.ItemFlagsStack.pop_back();
8256
g.CurrentItemFlags = g.ItemFlagsStack.back();
8257
if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
8258
g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
8259
}
8260
8261
// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
8262
// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
8263
// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
8264
void ImGui::BeginDisabledOverrideReenable()
8265
{
8266
ImGuiContext& g = *GImGui;
8267
IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
8268
g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup = g.Style.Alpha;
8269
g.Style.Alpha = g.DisabledAlphaBackup;
8270
g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
8271
g.ItemFlagsStack.push_back(g.CurrentItemFlags);
8272
g.DisabledStackSize++;
8273
}
8274
8275
void ImGui::EndDisabledOverrideReenable()
8276
{
8277
ImGuiContext& g = *GImGui;
8278
g.DisabledStackSize--;
8279
IM_ASSERT(g.DisabledStackSize > 0);
8280
g.ItemFlagsStack.pop_back();
8281
g.CurrentItemFlags = g.ItemFlagsStack.back();
8282
g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup;
8283
}
8284
8285
// ATTENTION THIS IS IN LEGACY LOCAL SPACE.
8286
void ImGui::PushTextWrapPos(float wrap_local_pos_x)
8287
{
8288
ImGuiContext& g = *GImGui;
8289
ImGuiWindow* window = g.CurrentWindow;
8290
window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
8291
window->DC.TextWrapPos = wrap_local_pos_x;
8292
}
8293
8294
void ImGui::PopTextWrapPos()
8295
{
8296
ImGuiContext& g = *GImGui;
8297
ImGuiWindow* window = g.CurrentWindow;
8298
if (window->DC.TextWrapPosStack.Size <= 0)
8299
{
8300
IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
8301
return;
8302
}
8303
window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
8304
window->DC.TextWrapPosStack.pop_back();
8305
}
8306
8307
static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)
8308
{
8309
ImGuiWindow* last_window = NULL;
8310
while (last_window != window)
8311
{
8312
last_window = window;
8313
window = window->RootWindow;
8314
if (popup_hierarchy)
8315
window = window->RootWindowPopupTree;
8316
}
8317
return window;
8318
}
8319
8320
bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy)
8321
{
8322
ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);
8323
if (window_root == potential_parent)
8324
return true;
8325
while (window != NULL)
8326
{
8327
if (window == potential_parent)
8328
return true;
8329
if (window == window_root) // end of chain
8330
return false;
8331
window = window->ParentWindow;
8332
}
8333
return false;
8334
}
8335
8336
bool ImGui::IsWindowInBeginStack(ImGuiWindow* window)
8337
{
8338
ImGuiContext& g = *GImGui;
8339
for (int n = g.CurrentWindowStack.Size - 1; n >= 0; n--)
8340
if (g.CurrentWindowStack[n].Window == window)
8341
return true;
8342
return false;
8343
}
8344
8345
bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
8346
{
8347
if (window->RootWindow == potential_parent)
8348
return true;
8349
while (window != NULL)
8350
{
8351
if (window == potential_parent)
8352
return true;
8353
window = window->ParentWindowInBeginStack;
8354
}
8355
return false;
8356
}
8357
8358
bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
8359
{
8360
ImGuiContext& g = *GImGui;
8361
8362
// 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
8363
const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
8364
if (display_layer_delta != 0)
8365
return display_layer_delta > 0;
8366
8367
for (int i = g.Windows.Size - 1; i >= 0; i--)
8368
{
8369
ImGuiWindow* candidate_window = g.Windows[i];
8370
if (candidate_window == potential_above)
8371
return true;
8372
if (candidate_window == potential_below)
8373
return false;
8374
}
8375
return false;
8376
}
8377
8378
// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
8379
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
8380
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
8381
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
8382
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
8383
{
8384
ImGuiContext& g = *GImGui;
8385
IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
8386
8387
ImGuiWindow* ref_window = g.HoveredWindow;
8388
ImGuiWindow* cur_window = g.CurrentWindow;
8389
if (ref_window == NULL)
8390
return false;
8391
8392
if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
8393
{
8394
IM_ASSERT(cur_window); // Not inside a Begin()/End()
8395
const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
8396
if (flags & ImGuiHoveredFlags_RootWindow)
8397
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
8398
8399
bool result;
8400
if (flags & ImGuiHoveredFlags_ChildWindows)
8401
result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
8402
else
8403
result = (ref_window == cur_window);
8404
if (!result)
8405
return false;
8406
}
8407
8408
if (!IsWindowContentHoverable(ref_window, flags))
8409
return false;
8410
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
8411
if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
8412
return false;
8413
8414
// When changing hovered window we requires a bit of stationary delay before activating hover timer.
8415
// FIXME: We don't support delay other than stationary one for now, other delay would need a way
8416
// to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
8417
// for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
8418
// We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
8419
if (flags & ImGuiHoveredFlags_ForTooltip)
8420
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
8421
if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
8422
return false;
8423
8424
return true;
8425
}
8426
8427
float ImGui::GetWindowWidth()
8428
{
8429
ImGuiWindow* window = GImGui->CurrentWindow;
8430
return window->Size.x;
8431
}
8432
8433
float ImGui::GetWindowHeight()
8434
{
8435
ImGuiWindow* window = GImGui->CurrentWindow;
8436
return window->Size.y;
8437
}
8438
8439
ImVec2 ImGui::GetWindowPos()
8440
{
8441
ImGuiContext& g = *GImGui;
8442
ImGuiWindow* window = g.CurrentWindow;
8443
return window->Pos;
8444
}
8445
8446
void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
8447
{
8448
// Test condition (NB: bit 0 is always true) and clear flags for next time
8449
if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
8450
return;
8451
8452
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8453
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8454
window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
8455
8456
// Set
8457
const ImVec2 old_pos = window->Pos;
8458
window->Pos = ImTrunc(pos);
8459
ImVec2 offset = window->Pos - old_pos;
8460
if (offset.x == 0.0f && offset.y == 0.0f)
8461
return;
8462
MarkIniSettingsDirty(window);
8463
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
8464
window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
8465
window->DC.IdealMaxPos += offset;
8466
window->DC.CursorStartPos += offset;
8467
}
8468
8469
void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
8470
{
8471
ImGuiWindow* window = GetCurrentWindowRead();
8472
SetWindowPos(window, pos, cond);
8473
}
8474
8475
void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
8476
{
8477
if (ImGuiWindow* window = FindWindowByName(name))
8478
SetWindowPos(window, pos, cond);
8479
}
8480
8481
ImVec2 ImGui::GetWindowSize()
8482
{
8483
ImGuiWindow* window = GetCurrentWindowRead();
8484
return window->Size;
8485
}
8486
8487
void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
8488
{
8489
// Test condition (NB: bit 0 is always true) and clear flags for next time
8490
if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
8491
return;
8492
8493
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8494
window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8495
8496
// Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
8497
if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8498
window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
8499
if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8500
window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
8501
8502
// Set
8503
ImVec2 old_size = window->SizeFull;
8504
if (size.x <= 0.0f)
8505
window->AutoFitOnlyGrows = false;
8506
else
8507
window->SizeFull.x = IM_TRUNC(size.x);
8508
if (size.y <= 0.0f)
8509
window->AutoFitOnlyGrows = false;
8510
else
8511
window->SizeFull.y = IM_TRUNC(size.y);
8512
if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
8513
MarkIniSettingsDirty(window);
8514
}
8515
8516
void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
8517
{
8518
SetWindowSize(GImGui->CurrentWindow, size, cond);
8519
}
8520
8521
void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
8522
{
8523
if (ImGuiWindow* window = FindWindowByName(name))
8524
SetWindowSize(window, size, cond);
8525
}
8526
8527
void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
8528
{
8529
// Test condition (NB: bit 0 is always true) and clear flags for next time
8530
if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
8531
return;
8532
window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8533
8534
// Queue applying in Begin()
8535
if (window->WantCollapseToggle)
8536
window->Collapsed ^= 1;
8537
window->WantCollapseToggle = (window->Collapsed != collapsed);
8538
}
8539
8540
void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
8541
{
8542
IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
8543
window->HitTestHoleSize = ImVec2ih(size);
8544
window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
8545
}
8546
8547
void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
8548
{
8549
window->Hidden = window->SkipItems = true;
8550
window->HiddenFramesCanSkipItems = 1;
8551
}
8552
8553
void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
8554
{
8555
SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
8556
}
8557
8558
bool ImGui::IsWindowCollapsed()
8559
{
8560
ImGuiWindow* window = GetCurrentWindowRead();
8561
return window->Collapsed;
8562
}
8563
8564
bool ImGui::IsWindowAppearing()
8565
{
8566
ImGuiWindow* window = GetCurrentWindowRead();
8567
return window->Appearing;
8568
}
8569
8570
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
8571
{
8572
if (ImGuiWindow* window = FindWindowByName(name))
8573
SetWindowCollapsed(window, collapsed, cond);
8574
}
8575
8576
void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
8577
{
8578
ImGuiContext& g = *GImGui;
8579
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8580
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasPos;
8581
g.NextWindowData.PosVal = pos;
8582
g.NextWindowData.PosPivotVal = pivot;
8583
g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
8584
}
8585
8586
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
8587
{
8588
ImGuiContext& g = *GImGui;
8589
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8590
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSize;
8591
g.NextWindowData.SizeVal = size;
8592
g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
8593
}
8594
8595
// For each axis:
8596
// - 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.
8597
// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
8598
// - See "Demo->Examples->Constrained-resizing window" for examples.
8599
void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
8600
{
8601
ImGuiContext& g = *GImGui;
8602
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
8603
g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
8604
g.NextWindowData.SizeCallback = custom_callback;
8605
g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
8606
}
8607
8608
// Content size = inner scrollable rectangle, padded with WindowPadding.
8609
// SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
8610
void ImGui::SetNextWindowContentSize(const ImVec2& size)
8611
{
8612
ImGuiContext& g = *GImGui;
8613
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasContentSize;
8614
g.NextWindowData.ContentSizeVal = ImTrunc(size);
8615
}
8616
8617
void ImGui::SetNextWindowScroll(const ImVec2& scroll)
8618
{
8619
ImGuiContext& g = *GImGui;
8620
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasScroll;
8621
g.NextWindowData.ScrollVal = scroll;
8622
}
8623
8624
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
8625
{
8626
ImGuiContext& g = *GImGui;
8627
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8628
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasCollapsed;
8629
g.NextWindowData.CollapsedVal = collapsed;
8630
g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
8631
}
8632
8633
void ImGui::SetNextWindowBgAlpha(float alpha)
8634
{
8635
ImGuiContext& g = *GImGui;
8636
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasBgAlpha;
8637
g.NextWindowData.BgAlphaVal = alpha;
8638
}
8639
8640
// This is experimental and meant to be a toy for exploring a future/wider range of features.
8641
void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
8642
{
8643
ImGuiContext& g = *GImGui;
8644
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
8645
g.NextWindowData.RefreshFlagsVal = flags;
8646
}
8647
8648
ImDrawList* ImGui::GetWindowDrawList()
8649
{
8650
ImGuiWindow* window = GetCurrentWindow();
8651
return window->DrawList;
8652
}
8653
8654
ImFont* ImGui::GetFont()
8655
{
8656
return GImGui->Font;
8657
}
8658
8659
ImFontBaked* ImGui::GetFontBaked()
8660
{
8661
return GImGui->FontBaked;
8662
}
8663
8664
// Get current font size (= height in pixels) of current font, with global scale factors applied.
8665
// - Use style.FontSizeBase to get value before global scale factors.
8666
// - recap: ImGui::GetFontSize() == style.FontSizeBase * (style.FontScaleMain * style.FontScaleDpi * other_scaling_factors)
8667
float ImGui::GetFontSize()
8668
{
8669
return GImGui->FontSize;
8670
}
8671
8672
float ImGui::GetFontWeight()
8673
{
8674
return GImGui->FontWeight;
8675
}
8676
8677
ImVec2 ImGui::GetFontTexUvWhitePixel()
8678
{
8679
return GImGui->DrawListSharedData.TexUvWhitePixel;
8680
}
8681
8682
// Prefer using PushFont(NULL, style.FontSizeBase * factor), or use style.FontScaleMain to scale all windows.
8683
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8684
void ImGui::SetWindowFontScale(float scale)
8685
{
8686
IM_ASSERT(scale > 0.0f);
8687
ImGuiWindow* window = GetCurrentWindow();
8688
window->FontWindowScale = scale;
8689
UpdateCurrentFontSize(0.0f);
8690
}
8691
#endif
8692
8693
void ImGui::PushFocusScope(ImGuiID id)
8694
{
8695
ImGuiContext& g = *GImGui;
8696
ImGuiFocusScopeData data;
8697
data.ID = id;
8698
data.WindowID = g.CurrentWindow->ID;
8699
g.FocusScopeStack.push_back(data);
8700
g.CurrentFocusScopeId = id;
8701
}
8702
8703
void ImGui::PopFocusScope()
8704
{
8705
ImGuiContext& g = *GImGui;
8706
if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
8707
{
8708
IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
8709
return;
8710
}
8711
g.FocusScopeStack.pop_back();
8712
g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
8713
}
8714
8715
void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
8716
{
8717
ImGuiContext& g = *GImGui;
8718
g.NavFocusScopeId = focus_scope_id;
8719
g.NavFocusRoute.resize(0); // Invalidate
8720
if (focus_scope_id == 0)
8721
return;
8722
IM_ASSERT(g.NavWindow != NULL);
8723
8724
// Store current path (in reverse order)
8725
if (focus_scope_id == g.CurrentFocusScopeId)
8726
{
8727
// Top of focus stack contains local focus scopes inside current window
8728
for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
8729
g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
8730
}
8731
else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
8732
g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
8733
else
8734
return;
8735
8736
// Then follow on manually set ParentWindowForFocusRoute field (#6798)
8737
for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
8738
g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
8739
IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
8740
}
8741
8742
// Focus = move navigation cursor, set scrolling, set focus window.
8743
void ImGui::FocusItem()
8744
{
8745
ImGuiContext& g = *GImGui;
8746
ImGuiWindow* window = g.CurrentWindow;
8747
IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
8748
if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
8749
{
8750
IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
8751
return;
8752
}
8753
8754
ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
8755
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8756
SetNavWindow(window);
8757
NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
8758
NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8759
}
8760
8761
void ImGui::ActivateItemByID(ImGuiID id)
8762
{
8763
ImGuiContext& g = *GImGui;
8764
g.NavNextActivateId = id;
8765
g.NavNextActivateFlags = ImGuiActivateFlags_None;
8766
}
8767
8768
// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
8769
// But ActivateItem() should function without altering scroll/focus?
8770
void ImGui::SetKeyboardFocusHere(int offset)
8771
{
8772
ImGuiContext& g = *GImGui;
8773
ImGuiWindow* window = g.CurrentWindow;
8774
IM_ASSERT(offset >= -1); // -1 is allowed but not below
8775
IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
8776
8777
// It makes sense in the vast majority of cases to never interrupt a drag and drop.
8778
// When we refactor this function into ActivateItem() we may want to make this an option.
8779
// MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
8780
// is also automatically dropped in the event g.ActiveId is stolen.
8781
if (g.DragDropActive || g.MovingWindow != NULL)
8782
{
8783
IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
8784
return;
8785
}
8786
8787
SetNavWindow(window);
8788
8789
ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
8790
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8791
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.
8792
if (offset == -1)
8793
{
8794
NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8795
}
8796
else
8797
{
8798
g.NavTabbingDir = 1;
8799
g.NavTabbingCounter = offset + 1;
8800
}
8801
}
8802
8803
void ImGui::SetItemDefaultFocus()
8804
{
8805
ImGuiContext& g = *GImGui;
8806
ImGuiWindow* window = g.CurrentWindow;
8807
if (!window->Appearing)
8808
return;
8809
if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
8810
return;
8811
8812
g.NavInitRequest = false;
8813
NavApplyItemToResult(&g.NavInitResult);
8814
NavUpdateAnyRequestFlag();
8815
8816
// Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
8817
if (!window->ClipRect.Contains(g.LastItemData.Rect))
8818
ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
8819
}
8820
8821
void ImGui::SetStateStorage(ImGuiStorage* tree)
8822
{
8823
ImGuiWindow* window = GImGui->CurrentWindow;
8824
window->DC.StateStorage = tree ? tree : &window->StateStorage;
8825
}
8826
8827
ImGuiStorage* ImGui::GetStateStorage()
8828
{
8829
ImGuiWindow* window = GImGui->CurrentWindow;
8830
return window->DC.StateStorage;
8831
}
8832
8833
bool ImGui::IsRectVisible(const ImVec2& size)
8834
{
8835
ImGuiWindow* window = GImGui->CurrentWindow;
8836
return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
8837
}
8838
8839
bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
8840
{
8841
ImGuiWindow* window = GImGui->CurrentWindow;
8842
return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
8843
}
8844
8845
//-----------------------------------------------------------------------------
8846
// [SECTION] FONTS, TEXTURES
8847
//-----------------------------------------------------------------------------
8848
// Most of the relevant font logic is in imgui_draw.cpp.
8849
// Those are high-level support functions.
8850
//-----------------------------------------------------------------------------
8851
// - UpdateTexturesNewFrame() [Internal]
8852
// - UpdateTexturesEndFrame() [Internal]
8853
// - UpdateFontsNewFrame() [Internal]
8854
// - UpdateFontsEndFrame() [Internal]
8855
// - GetDefaultFont() [Internal]
8856
// - RegisterUserTexture() [Internal]
8857
// - UnregisterUserTexture() [Internal]
8858
// - RegisterFontAtlas() [Internal]
8859
// - UnregisterFontAtlas() [Internal]
8860
// - SetCurrentFont() [Internal]
8861
// - UpdateCurrentFontSize() [Internal]
8862
// - SetFontRasterizerDensity() [Internal]
8863
// - PushFont()
8864
// - PopFont()
8865
//-----------------------------------------------------------------------------
8866
8867
static void ImGui::UpdateTexturesNewFrame()
8868
{
8869
// Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count.
8870
ImGuiContext& g = *GImGui;
8871
const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
8872
for (ImFontAtlas* atlas : g.FontAtlases)
8873
{
8874
if (atlas->OwnerContext == &g)
8875
{
8876
ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures);
8877
}
8878
else
8879
{
8880
// (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it.
8881
// Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context.
8882
// (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that.
8883
// (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures.
8884
IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1);
8885
IM_ASSERT(atlas->RendererHasTextures == has_textures);
8886
}
8887
}
8888
}
8889
8890
// Build a single texture list
8891
static void ImGui::UpdateTexturesEndFrame()
8892
{
8893
ImGuiContext& g = *GImGui;
8894
g.PlatformIO.Textures.resize(0);
8895
for (ImFontAtlas* atlas : g.FontAtlases)
8896
for (ImTextureData* tex : atlas->TexList)
8897
{
8898
// We provide this information so backends can decide whether to destroy textures.
8899
// This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized.
8900
tex->RefCount = (unsigned short)atlas->RefCount;
8901
g.PlatformIO.Textures.push_back(tex);
8902
}
8903
for (ImTextureData* tex : g.UserTextures)
8904
g.PlatformIO.Textures.push_back(tex);
8905
}
8906
8907
void ImGui::UpdateFontsNewFrame()
8908
{
8909
ImGuiContext& g = *GImGui;
8910
if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8911
for (ImFontAtlas* atlas : g.FontAtlases)
8912
atlas->Locked = true;
8913
8914
if (g.Style._NextFrameFontSizeBase != 0.0f)
8915
{
8916
g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase;
8917
g.Style._NextFrameFontSizeBase = 0.0f;
8918
}
8919
8920
// Apply default font size the first time
8921
ImFont* font = ImGui::GetDefaultFont();
8922
if (g.Style.FontSizeBase <= 0.0f)
8923
g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE_BASE);
8924
8925
// Set initial font
8926
g.Font = font;
8927
g.FontSizeBase = g.Style.FontSizeBase;
8928
g.FontSize = 0.0f;
8929
ImFontStackData font_stack_data = { font, g.Style.FontSizeBase, g.Style.FontSizeBase }; // <--- Will restore FontSize
8930
SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0f, font_stack_data.FontWeight); // <--- but use 0.0f to enable scale
8931
g.FontStack.push_back(font_stack_data);
8932
IM_ASSERT(g.Font->IsLoaded());
8933
}
8934
8935
void ImGui::UpdateFontsEndFrame()
8936
{
8937
PopFont();
8938
}
8939
8940
ImFont* ImGui::GetDefaultFont()
8941
{
8942
ImGuiContext& g = *GImGui;
8943
ImFontAtlas* atlas = g.IO.Fonts;
8944
if (atlas->Builder == NULL || atlas->Fonts.Size == 0)
8945
ImFontAtlasBuildMain(atlas);
8946
return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];
8947
}
8948
8949
// EXPERIMENTAL: DO NOT USE YET.
8950
void ImGui::RegisterUserTexture(ImTextureData* tex)
8951
{
8952
ImGuiContext& g = *GImGui;
8953
tex->RefCount++;
8954
g.UserTextures.push_back(tex);
8955
}
8956
8957
void ImGui::UnregisterUserTexture(ImTextureData* tex)
8958
{
8959
ImGuiContext& g = *GImGui;
8960
IM_ASSERT(tex->RefCount > 0);
8961
tex->RefCount--;
8962
g.UserTextures.find_erase(tex);
8963
}
8964
8965
void ImGui::RegisterFontAtlas(ImFontAtlas* atlas)
8966
{
8967
ImGuiContext& g = *GImGui;
8968
if (g.FontAtlases.Size == 0)
8969
IM_ASSERT(atlas == g.IO.Fonts);
8970
atlas->RefCount++;
8971
g.FontAtlases.push_back(atlas);
8972
ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData);
8973
for (ImTextureData* tex : atlas->TexList)
8974
tex->RefCount = (unsigned short)atlas->RefCount;
8975
}
8976
8977
void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas)
8978
{
8979
ImGuiContext& g = *GImGui;
8980
IM_ASSERT(atlas->RefCount > 0);
8981
ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData);
8982
g.FontAtlases.find_erase(atlas);
8983
atlas->RefCount--;
8984
for (ImTextureData* tex : atlas->TexList)
8985
tex->RefCount = (unsigned short)atlas->RefCount;
8986
}
8987
8988
// Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList.
8989
// - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls.
8990
// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
8991
// - Some code paths never really fully worked with multiple atlas textures.
8992
// - The right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture()
8993
// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
8994
// because we have a concrete need and a test bed for multiple atlas textures.
8995
// FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ?
8996
void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling, float font_weight)
8997
{
8998
ImGuiContext& g = *GImGui;
8999
g.Font = font;
9000
g.FontSizeBase = font_size_before_scaling;
9001
g.FontWeight = font_weight;
9002
g.DrawListSharedData.FontWeight = g.FontWeight;
9003
UpdateCurrentFontSize(font_size_after_scaling);
9004
9005
if (font != NULL)
9006
{
9007
IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
9008
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
9009
IM_ASSERT(font->Scale > 0.0f);
9010
#endif
9011
ImFontAtlas* atlas = font->OwnerAtlas;
9012
g.DrawListSharedData.FontAtlas = atlas;
9013
g.DrawListSharedData.Font = font;
9014
ImFontAtlasUpdateDrawListsSharedData(atlas);
9015
if (g.CurrentWindow != NULL)
9016
g.CurrentWindow->DrawList->_SetTexture(atlas->TexRef);
9017
}
9018
}
9019
9020
void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)
9021
{
9022
ImGuiContext& g = *GImGui;
9023
ImGuiWindow* window = g.CurrentWindow;
9024
9025
g.Style.FontSizeBase = g.FontSizeBase;
9026
9027
// Early out to avoid hidden window keeping bakes referenced and out of GC reach.
9028
// However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching.
9029
// FIXME: perhaps g.FontSize should be updated?
9030
if (window != NULL && window->SkipItems)
9031
{
9032
ImGuiTable* table = g.CurrentTable;
9033
if (table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false)) // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data.
9034
return;
9035
}
9036
9037
// Restoring is pretty much only used by PopFont()
9038
float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f;
9039
if (final_size == 0.0f)
9040
{
9041
final_size = g.FontSizeBase;
9042
9043
// Global scale factors
9044
final_size *= g.Style.FontScaleMain; // Main global scale factor
9045
final_size *= g.Style.FontScaleDpi; // Per-monitor/viewport DPI scale factor, automatically updated when io.ConfigDpiScaleFonts is enabled.
9046
9047
// Window scale (mostly obsolete now)
9048
if (window != NULL)
9049
final_size *= window->FontWindowScale;
9050
9051
// Legacy scale factors
9052
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
9053
final_size *= g.IO.FontGlobalScale; // Use style.FontScaleMain instead!
9054
if (g.Font != NULL)
9055
final_size *= g.Font->Scale; // Was never really useful.
9056
#endif
9057
}
9058
9059
// Round font size
9060
// - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
9061
// - We may support it better later and remove this rounding.
9062
final_size = GetRoundedFontSize(final_size);
9063
final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX);
9064
if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures))
9065
g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity;
9066
g.FontSize = final_size;
9067
g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size, g.FontWeight) : NULL;
9068
g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
9069
g.DrawListSharedData.FontSize = g.FontSize;
9070
g.DrawListSharedData.FontScale = g.FontBakedScale;
9071
}
9072
9073
// Exposed in case user may want to override setting density.
9074
// IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it.
9075
void ImGui::SetFontRasterizerDensity(float rasterizer_density)
9076
{
9077
ImGuiContext& g = *GImGui;
9078
IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures);
9079
if (g.FontRasterizerDensity == rasterizer_density)
9080
return;
9081
g.FontRasterizerDensity = rasterizer_density;
9082
UpdateCurrentFontSize(0.0f);
9083
}
9084
9085
// If you want to scale an existing font size! Read comments in imgui.h!
9086
void ImGui::PushFont(ImFont* font, float font_size_base, float font_weight)
9087
{
9088
ImGuiContext& g = *GImGui;
9089
if (font == NULL) // Before 1.92 (June 2025), PushFont(NULL) == PushFont(GetDefaultFont())
9090
font = g.Font;
9091
IM_ASSERT(font != NULL);
9092
IM_ASSERT(font_size_base >= 0.0f);
9093
9094
g.FontStack.push_back({ g.Font, g.FontSizeBase, g.FontSize, g.FontWeight });
9095
if (font_size_base == 0.0f)
9096
font_size_base = g.FontSizeBase; // Keep current font size
9097
if (font_weight <= 0.0f)
9098
font_weight = (font_weight == 0.0f) ? font->DefaultWeight : g.FontWeight;
9099
SetCurrentFont(font, font_size_base, 0.0f, font_weight);
9100
}
9101
9102
void ImGui::PopFont()
9103
{
9104
ImGuiContext& g = *GImGui;
9105
if (g.FontStack.Size <= 0)
9106
{
9107
IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
9108
return;
9109
}
9110
ImFontStackData* font_stack_data = &g.FontStack.back();
9111
SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling, font_stack_data->FontWeight);
9112
g.FontStack.pop_back();
9113
}
9114
9115
//-----------------------------------------------------------------------------
9116
// [SECTION] ID STACK
9117
//-----------------------------------------------------------------------------
9118
9119
// This is one of the very rare legacy case where we use ImGuiWindow methods,
9120
// it should ideally be flattened at some point but it's been used a lots by widgets.
9121
IM_MSVC_RUNTIME_CHECKS_OFF
9122
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
9123
{
9124
ImGuiID seed = IDStack.back();
9125
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9126
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9127
ImGuiContext& g = *Ctx;
9128
if (g.DebugHookIdInfoId == id)
9129
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9130
#endif
9131
return id;
9132
}
9133
9134
ImGuiID ImGuiWindow::GetID(const void* ptr)
9135
{
9136
ImGuiID seed = IDStack.back();
9137
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
9138
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9139
ImGuiContext& g = *Ctx;
9140
if (g.DebugHookIdInfoId == id)
9141
ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
9142
#endif
9143
return id;
9144
}
9145
9146
ImGuiID ImGuiWindow::GetID(int n)
9147
{
9148
ImGuiID seed = IDStack.back();
9149
ImGuiID id = ImHashData(&n, sizeof(n), seed);
9150
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9151
ImGuiContext& g = *Ctx;
9152
if (g.DebugHookIdInfoId == id)
9153
ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9154
#endif
9155
return id;
9156
}
9157
9158
// This is only used in rare/specific situations to manufacture an ID out of nowhere.
9159
// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
9160
ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)
9161
{
9162
ImGuiID seed = IDStack.back();
9163
ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs);
9164
ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed);
9165
return id;
9166
}
9167
9168
// "
9169
ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
9170
{
9171
ImGuiID seed = IDStack.back();
9172
ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
9173
ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
9174
return id;
9175
}
9176
9177
void ImGui::PushID(const char* str_id)
9178
{
9179
ImGuiContext& g = *GImGui;
9180
ImGuiWindow* window = g.CurrentWindow;
9181
ImGuiID id = window->GetID(str_id);
9182
window->IDStack.push_back(id);
9183
}
9184
9185
void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
9186
{
9187
ImGuiContext& g = *GImGui;
9188
ImGuiWindow* window = g.CurrentWindow;
9189
ImGuiID id = window->GetID(str_id_begin, str_id_end);
9190
window->IDStack.push_back(id);
9191
}
9192
9193
void ImGui::PushID(const void* ptr_id)
9194
{
9195
ImGuiContext& g = *GImGui;
9196
ImGuiWindow* window = g.CurrentWindow;
9197
ImGuiID id = window->GetID(ptr_id);
9198
window->IDStack.push_back(id);
9199
}
9200
9201
void ImGui::PushID(int int_id)
9202
{
9203
ImGuiContext& g = *GImGui;
9204
ImGuiWindow* window = g.CurrentWindow;
9205
ImGuiID id = window->GetID(int_id);
9206
window->IDStack.push_back(id);
9207
}
9208
9209
// Push a given id value ignoring the ID stack as a seed.
9210
void ImGui::PushOverrideID(ImGuiID id)
9211
{
9212
ImGuiContext& g = *GImGui;
9213
ImGuiWindow* window = g.CurrentWindow;
9214
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9215
if (g.DebugHookIdInfoId == id)
9216
DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
9217
#endif
9218
window->IDStack.push_back(id);
9219
}
9220
9221
// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
9222
// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
9223
// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
9224
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
9225
{
9226
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9227
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9228
ImGuiContext& g = *GImGui;
9229
if (g.DebugHookIdInfoId == id)
9230
DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9231
#endif
9232
return id;
9233
}
9234
9235
ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
9236
{
9237
ImGuiID id = ImHashData(&n, sizeof(n), seed);
9238
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9239
ImGuiContext& g = *GImGui;
9240
if (g.DebugHookIdInfoId == id)
9241
DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9242
#endif
9243
return id;
9244
}
9245
9246
void ImGui::PopID()
9247
{
9248
ImGuiWindow* window = GImGui->CurrentWindow;
9249
if (window->IDStack.Size <= 1)
9250
{
9251
IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
9252
return;
9253
}
9254
window->IDStack.pop_back();
9255
}
9256
9257
ImGuiID ImGui::GetID(const char* str_id)
9258
{
9259
ImGuiWindow* window = GImGui->CurrentWindow;
9260
return window->GetID(str_id);
9261
}
9262
9263
ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
9264
{
9265
ImGuiWindow* window = GImGui->CurrentWindow;
9266
return window->GetID(str_id_begin, str_id_end);
9267
}
9268
9269
ImGuiID ImGui::GetID(const void* ptr_id)
9270
{
9271
ImGuiWindow* window = GImGui->CurrentWindow;
9272
return window->GetID(ptr_id);
9273
}
9274
9275
ImGuiID ImGui::GetID(int int_id)
9276
{
9277
ImGuiWindow* window = GImGui->CurrentWindow;
9278
return window->GetID(int_id);
9279
}
9280
IM_MSVC_RUNTIME_CHECKS_RESTORE
9281
9282
//-----------------------------------------------------------------------------
9283
// [SECTION] INPUTS
9284
//-----------------------------------------------------------------------------
9285
// - GetModForLRModKey() [Internal]
9286
// - FixupKeyChord() [Internal]
9287
// - GetKeyData() [Internal]
9288
// - GetKeyIndex() [Internal]
9289
// - GetKeyName()
9290
// - GetKeyChordName() [Internal]
9291
// - CalcTypematicRepeatAmount() [Internal]
9292
// - GetTypematicRepeatRate() [Internal]
9293
// - GetKeyPressedAmount() [Internal]
9294
// - GetKeyMagnitude2d() [Internal]
9295
//-----------------------------------------------------------------------------
9296
// - UpdateKeyRoutingTable() [Internal]
9297
// - GetRoutingIdFromOwnerId() [Internal]
9298
// - GetShortcutRoutingData() [Internal]
9299
// - CalcRoutingScore() [Internal]
9300
// - SetShortcutRouting() [Internal]
9301
// - TestShortcutRouting() [Internal]
9302
//-----------------------------------------------------------------------------
9303
// - IsKeyDown()
9304
// - IsKeyPressed()
9305
// - IsKeyReleased()
9306
//-----------------------------------------------------------------------------
9307
// - IsMouseDown()
9308
// - IsMouseClicked()
9309
// - IsMouseReleased()
9310
// - IsMouseDoubleClicked()
9311
// - GetMouseClickedCount()
9312
// - IsMouseHoveringRect() [Internal]
9313
// - IsMouseDragPastThreshold() [Internal]
9314
// - IsMouseDragging()
9315
// - GetMousePos()
9316
// - SetMousePos() [Internal]
9317
// - GetMousePosOnOpeningCurrentPopup()
9318
// - IsMousePosValid()
9319
// - IsAnyMouseDown()
9320
// - GetMouseDragDelta()
9321
// - ResetMouseDragDelta()
9322
// - GetMouseCursor()
9323
// - SetMouseCursor()
9324
//-----------------------------------------------------------------------------
9325
// - UpdateAliasKey()
9326
// - GetMergedModsFromKeys()
9327
// - UpdateKeyboardInputs()
9328
// - UpdateMouseInputs()
9329
//-----------------------------------------------------------------------------
9330
// - LockWheelingWindow [Internal]
9331
// - FindBestWheelingWindow [Internal]
9332
// - UpdateMouseWheel() [Internal]
9333
//-----------------------------------------------------------------------------
9334
// - SetNextFrameWantCaptureKeyboard()
9335
// - SetNextFrameWantCaptureMouse()
9336
//-----------------------------------------------------------------------------
9337
// - GetInputSourceName() [Internal]
9338
// - DebugPrintInputEvent() [Internal]
9339
// - UpdateInputEvents() [Internal]
9340
//-----------------------------------------------------------------------------
9341
// - GetKeyOwner() [Internal]
9342
// - TestKeyOwner() [Internal]
9343
// - SetKeyOwner() [Internal]
9344
// - SetItemKeyOwner() [Internal]
9345
// - Shortcut() [Internal]
9346
//-----------------------------------------------------------------------------
9347
9348
static ImGuiKeyChord GetModForLRModKey(ImGuiKey key)
9349
{
9350
if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
9351
return ImGuiMod_Ctrl;
9352
if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
9353
return ImGuiMod_Shift;
9354
if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
9355
return ImGuiMod_Alt;
9356
if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
9357
return ImGuiMod_Super;
9358
return ImGuiMod_None;
9359
}
9360
9361
ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
9362
{
9363
// Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9364
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9365
if (IsLRModKey(key))
9366
key_chord |= GetModForLRModKey(key);
9367
return key_chord;
9368
}
9369
9370
ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
9371
{
9372
ImGuiContext& g = *ctx;
9373
9374
// Special storage location for mods
9375
if (key & ImGuiMod_Mask_)
9376
key = ConvertSingleModFlagToKey(key);
9377
9378
IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
9379
return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9380
}
9381
9382
// Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.
9383
static const char* const GKeyNames[] =
9384
{
9385
"Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
9386
"Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
9387
"LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
9388
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
9389
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9390
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
9391
"F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
9392
"Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
9393
"Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
9394
"Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
9395
"Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
9396
"KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
9397
"AppBack", "AppForward", "Oem102",
9398
"GamepadStart", "GamepadBack",
9399
"GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
9400
"GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
9401
"GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
9402
"GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
9403
"GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
9404
"MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
9405
"ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
9406
};
9407
IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_COUNTOF(GKeyNames));
9408
9409
const char* ImGui::GetKeyName(ImGuiKey key)
9410
{
9411
if (key == ImGuiKey_None)
9412
return "None";
9413
IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
9414
if (key & ImGuiMod_Mask_)
9415
key = ConvertSingleModFlagToKey(key);
9416
if (!IsNamedKey(key))
9417
return "Unknown";
9418
9419
return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
9420
}
9421
9422
// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
9423
// Lifetime of return value: valid until next call to same function.
9424
const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
9425
{
9426
ImGuiContext& g = *GImGui;
9427
9428
const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9429
if (IsLRModKey(key))
9430
key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
9431
ImFormatString(g.TempKeychordName, IM_COUNTOF(g.TempKeychordName), "%s%s%s%s%s",
9432
(key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
9433
(key_chord & ImGuiMod_Shift) ? "Shift+" : "",
9434
(key_chord & ImGuiMod_Alt) ? "Alt+" : "",
9435
(key_chord & ImGuiMod_Super) ? "Super+" : "",
9436
(key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
9437
size_t len;
9438
if (key == ImGuiKey_None && key_chord != 0)
9439
if ((len = ImStrlen(g.TempKeychordName)) != 0) // Remove trailing '+'
9440
g.TempKeychordName[len - 1] = 0;
9441
return g.TempKeychordName;
9442
}
9443
9444
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
9445
// t1 = current time (e.g.: g.Time)
9446
// An event is triggered at:
9447
// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
9448
int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
9449
{
9450
if (t1 == 0.0f)
9451
return 1;
9452
if (t0 >= t1)
9453
return 0;
9454
if (repeat_rate <= 0.0f)
9455
return t0 < repeat_delay && t1 >= repeat_delay;
9456
const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
9457
const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
9458
const int count = count_t1 - count_t0;
9459
return count;
9460
}
9461
9462
void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
9463
{
9464
ImGuiContext& g = *GImGui;
9465
switch (flags & ImGuiInputFlags_RepeatRateMask_)
9466
{
9467
case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
9468
case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
9469
case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
9470
}
9471
}
9472
9473
// Return value representing the number of presses in the last time period, for the given repeat rate
9474
// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
9475
int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
9476
{
9477
ImGuiContext& g = *GImGui;
9478
const ImGuiKeyData* key_data = GetKeyData(key);
9479
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)
9480
return 0;
9481
const float t = key_data->DownDuration;
9482
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
9483
}
9484
9485
// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
9486
ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
9487
{
9488
return ImVec2(
9489
GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
9490
GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
9491
}
9492
9493
// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
9494
// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
9495
// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
9496
// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
9497
static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
9498
{
9499
ImGuiContext& g = *GImGui;
9500
rt->EntriesNext.resize(0);
9501
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9502
{
9503
const int new_routing_start_idx = rt->EntriesNext.Size;
9504
ImGuiKeyRoutingData* routing_entry;
9505
for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
9506
{
9507
routing_entry = &rt->Entries[old_routing_idx];
9508
routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
9509
routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
9510
routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
9511
routing_entry->RoutingNextScore = 0;
9512
if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
9513
continue;
9514
rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
9515
9516
// Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
9517
// This is the result of previous frame's SetShortcutRouting() call.
9518
if (routing_entry->Mods == g.IO.KeyMods)
9519
{
9520
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
9521
if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
9522
{
9523
owner_data->OwnerCurr = routing_entry->RoutingCurr;
9524
//IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
9525
}
9526
}
9527
}
9528
9529
// Rewrite linked-list
9530
rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
9531
for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
9532
rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
9533
}
9534
rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
9535
}
9536
9537
// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
9538
static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
9539
{
9540
ImGuiContext& g = *GImGui;
9541
return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
9542
}
9543
9544
ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
9545
{
9546
// Majority of shortcuts will be Key + any number of Mods
9547
// We accept _Single_ mod with ImGuiKey_None.
9548
// - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal
9549
// - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal
9550
// - Shortcut(ImGuiMod_Ctrl); // Legal
9551
// - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal
9552
ImGuiContext& g = *GImGui;
9553
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
9554
ImGuiKeyRoutingData* routing_data;
9555
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9556
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9557
if (key == ImGuiKey_None)
9558
key = ConvertSingleModFlagToKey(mods);
9559
IM_ASSERT(IsNamedKey(key));
9560
9561
// Get (in the majority of case, the linked list will have one element so this should be 2 reads.
9562
// Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
9563
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
9564
{
9565
routing_data = &rt->Entries[idx];
9566
if (routing_data->Mods == mods)
9567
return routing_data;
9568
}
9569
9570
// Add to linked-list
9571
ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
9572
rt->Entries.push_back(ImGuiKeyRoutingData());
9573
routing_data = &rt->Entries[routing_data_idx];
9574
routing_data->Mods = (ImU16)mods;
9575
routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
9576
rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
9577
return routing_data;
9578
}
9579
9580
// Current score encoding
9581
// - 0: Never route
9582
// - 1: ImGuiInputFlags_RouteGlobal (lower priority)
9583
// - 100..199: ImGuiInputFlags_RouteFocused (if window in focus-stack)
9584
// 200: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
9585
// 300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active)
9586
// 400: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
9587
// - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority)
9588
// 'flags' should include an explicit routing policy
9589
static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
9590
{
9591
ImGuiContext& g = *GImGui;
9592
if (flags & ImGuiInputFlags_RouteFocused)
9593
{
9594
// ActiveID gets high priority
9595
// (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
9596
if (owner_id != 0 && g.ActiveId == owner_id)
9597
return 300;
9598
9599
// Score based on distance to focused window (lower is better)
9600
// Assuming both windows are submitting a routing request,
9601
// - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
9602
// - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
9603
// Assuming only WindowA is submitting a routing request,
9604
// - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
9605
// This essentially follow the window->ParentWindowForFocusRoute chain.
9606
if (focus_scope_id == 0)
9607
return 0;
9608
for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
9609
if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
9610
{
9611
if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id)
9612
return 599 - index_in_focus_path;
9613
else
9614
return 199 - index_in_focus_path;
9615
}
9616
return 0;
9617
}
9618
else if (flags & ImGuiInputFlags_RouteActive)
9619
{
9620
if (owner_id != 0 && g.ActiveId == owner_id)
9621
return 300;
9622
return 0;
9623
}
9624
else if (flags & ImGuiInputFlags_RouteGlobal)
9625
{
9626
if (flags & ImGuiInputFlags_RouteOverActive)
9627
return 400;
9628
if (owner_id != 0 && g.ActiveId == owner_id)
9629
return 300;
9630
if (flags & ImGuiInputFlags_RouteOverFocused)
9631
return 200;
9632
return 1;
9633
}
9634
IM_ASSERT(0);
9635
return 0;
9636
}
9637
9638
// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
9639
// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
9640
// - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character.
9641
static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
9642
{
9643
// Mimic 'ignore_char_inputs' logic in InputText()
9644
ImGuiContext& g = *GImGui;
9645
9646
// When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
9647
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9648
const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
9649
if (ignore_char_inputs)
9650
return false;
9651
9652
// Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
9653
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9654
if (key == ImGuiKey_None)
9655
return false;
9656
return g.KeysMayBeCharInput.TestBit(key);
9657
}
9658
9659
// Request a desired route for an input chord (key + mods).
9660
// Return true if the route is available this frame.
9661
// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
9662
// (Conceptually this does a "Submit for next frame" + "Test for current frame".
9663
// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
9664
bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
9665
{
9666
ImGuiContext& g = *GImGui;
9667
if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
9668
flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
9669
else
9670
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
9671
IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
9672
if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused))
9673
IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
9674
if (flags & ImGuiInputFlags_RouteOverActive)
9675
IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused));
9676
9677
// Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9678
key_chord = FixupKeyChord(key_chord);
9679
9680
// [DEBUG] Debug break requested by user
9681
if (g.DebugBreakInShortcutRouting == key_chord)
9682
IM_DEBUG_BREAK();
9683
9684
if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
9685
if (g.NavWindow == NULL)
9686
return false;
9687
9688
// Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
9689
if (flags & ImGuiInputFlags_RouteAlways)
9690
{
9691
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
9692
return true;
9693
}
9694
9695
// Specific culling when there's an active item.
9696
if (g.ActiveId != 0 && g.ActiveId != owner_id)
9697
{
9698
if (flags & ImGuiInputFlags_RouteActive)
9699
return false;
9700
9701
// Cull shortcuts with no modifiers when it could generate a character.
9702
// e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
9703
// but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
9704
// TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
9705
// (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
9706
if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
9707
{
9708
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
9709
return false;
9710
}
9711
9712
// ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
9713
if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
9714
{
9715
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9716
if (key == ImGuiKey_None)
9717
key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
9718
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
9719
return false;
9720
}
9721
}
9722
9723
// Where do we evaluate route for?
9724
ImGuiID focus_scope_id = g.CurrentFocusScopeId;
9725
if (flags & ImGuiInputFlags_RouteFromRootWindow)
9726
focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
9727
9728
const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
9729
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
9730
if (score == 0)
9731
return false;
9732
9733
// Submit routing for NEXT frame (assuming score is sufficient)
9734
// FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >).
9735
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
9736
//const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore);
9737
if (score > routing_data->RoutingNextScore)
9738
{
9739
routing_data->RoutingNext = owner_id;
9740
routing_data->RoutingNextScore = (ImU16)score;
9741
}
9742
9743
// Return routing state for CURRENT frame
9744
if (routing_data->RoutingCurr == owner_id)
9745
IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
9746
return routing_data->RoutingCurr == owner_id;
9747
}
9748
9749
// Currently unused by core (but used by tests)
9750
// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
9751
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
9752
{
9753
const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
9754
key_chord = FixupKeyChord(key_chord);
9755
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
9756
return routing_data->RoutingCurr == routing_id;
9757
}
9758
9759
// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
9760
// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
9761
bool ImGui::IsKeyDown(ImGuiKey key)
9762
{
9763
return IsKeyDown(key, ImGuiKeyOwner_Any);
9764
}
9765
9766
bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
9767
{
9768
const ImGuiKeyData* key_data = GetKeyData(key);
9769
if (!key_data->Down)
9770
return false;
9771
if (!TestKeyOwner(key, owner_id))
9772
return false;
9773
return true;
9774
}
9775
9776
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
9777
{
9778
return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9779
}
9780
9781
// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
9782
bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
9783
{
9784
const ImGuiKeyData* key_data = GetKeyData(key);
9785
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)
9786
return false;
9787
const float t = key_data->DownDuration;
9788
if (t < 0.0f)
9789
return false;
9790
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
9791
if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
9792
flags |= ImGuiInputFlags_Repeat;
9793
9794
bool pressed = (t == 0.0f);
9795
if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
9796
{
9797
float repeat_delay, repeat_rate;
9798
GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
9799
pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
9800
if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
9801
{
9802
// Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
9803
// Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
9804
ImGuiContext& g = *GImGui;
9805
double key_pressed_time = g.Time - t + 0.00001f;
9806
if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
9807
pressed = false;
9808
if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
9809
pressed = false;
9810
if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
9811
pressed = false;
9812
}
9813
}
9814
if (!pressed)
9815
return false;
9816
if (!TestKeyOwner(key, owner_id))
9817
return false;
9818
return true;
9819
}
9820
9821
bool ImGui::IsKeyReleased(ImGuiKey key)
9822
{
9823
return IsKeyReleased(key, ImGuiKeyOwner_Any);
9824
}
9825
9826
bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
9827
{
9828
const ImGuiKeyData* key_data = GetKeyData(key);
9829
if (key_data->DownDurationPrev < 0.0f || key_data->Down)
9830
return false;
9831
if (!TestKeyOwner(key, owner_id))
9832
return false;
9833
return true;
9834
}
9835
9836
bool ImGui::IsMouseDown(ImGuiMouseButton button)
9837
{
9838
ImGuiContext& g = *GImGui;
9839
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9840
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.
9841
}
9842
9843
bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
9844
{
9845
ImGuiContext& g = *GImGui;
9846
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9847
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.
9848
}
9849
9850
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
9851
{
9852
return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9853
}
9854
9855
bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
9856
{
9857
ImGuiContext& g = *GImGui;
9858
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9859
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)
9860
return false;
9861
const float t = g.IO.MouseDownDuration[button];
9862
if (t < 0.0f)
9863
return false;
9864
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
9865
9866
const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
9867
const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
9868
if (!pressed)
9869
return false;
9870
9871
if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
9872
return false;
9873
9874
return true;
9875
}
9876
9877
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
9878
{
9879
ImGuiContext& g = *GImGui;
9880
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9881
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
9882
}
9883
9884
bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
9885
{
9886
ImGuiContext& g = *GImGui;
9887
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9888
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
9889
}
9890
9891
// Use if you absolutely need to distinguish single-click from double-click by introducing a delay.
9892
// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test.
9893
// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
9894
bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay)
9895
{
9896
ImGuiContext& g = *GImGui;
9897
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9898
const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]);
9899
return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
9900
}
9901
9902
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
9903
{
9904
ImGuiContext& g = *GImGui;
9905
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9906
return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
9907
}
9908
9909
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
9910
{
9911
ImGuiContext& g = *GImGui;
9912
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9913
return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
9914
}
9915
9916
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
9917
{
9918
ImGuiContext& g = *GImGui;
9919
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9920
return g.IO.MouseClickedCount[button];
9921
}
9922
9923
// Test if mouse cursor is hovering given rectangle
9924
// NB- Rectangle is clipped by our current clip setting
9925
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
9926
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
9927
{
9928
ImGuiContext& g = *GImGui;
9929
9930
// Clip
9931
ImRect rect_clipped(r_min, r_max);
9932
if (clip)
9933
rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
9934
9935
// Hit testing, expanded for touch input
9936
if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
9937
return false;
9938
return true;
9939
}
9940
9941
// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
9942
// [Internal] This doesn't test if the button is pressed
9943
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
9944
{
9945
ImGuiContext& g = *GImGui;
9946
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9947
if (lock_threshold < 0.0f)
9948
lock_threshold = g.IO.MouseDragThreshold;
9949
return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
9950
}
9951
9952
bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
9953
{
9954
ImGuiContext& g = *GImGui;
9955
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
9956
if (!g.IO.MouseDown[button])
9957
return false;
9958
return IsMouseDragPastThreshold(button, lock_threshold);
9959
}
9960
9961
ImVec2 ImGui::GetMousePos()
9962
{
9963
ImGuiContext& g = *GImGui;
9964
return g.IO.MousePos;
9965
}
9966
9967
// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
9968
// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
9969
void ImGui::TeleportMousePos(const ImVec2& pos)
9970
{
9971
ImGuiContext& g = *GImGui;
9972
g.IO.MousePos = g.IO.MousePosPrev = pos;
9973
g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
9974
g.IO.WantSetMousePos = true;
9975
//IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
9976
}
9977
9978
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
9979
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
9980
{
9981
ImGuiContext& g = *GImGui;
9982
if (g.BeginPopupStack.Size > 0)
9983
return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
9984
return g.IO.MousePos;
9985
}
9986
9987
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
9988
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
9989
{
9990
// The assert is only to silence a false-positive in XCode Static Analysis.
9991
// 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).
9992
IM_ASSERT(GImGui != NULL);
9993
const float MOUSE_INVALID = -256000.0f;
9994
ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
9995
return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
9996
}
9997
9998
// [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.
9999
bool ImGui::IsAnyMouseDown()
10000
{
10001
ImGuiContext& g = *GImGui;
10002
for (int n = 0; n < IM_COUNTOF(g.IO.MouseDown); n++)
10003
if (g.IO.MouseDown[n])
10004
return true;
10005
return false;
10006
}
10007
10008
// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
10009
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
10010
// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
10011
ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
10012
{
10013
ImGuiContext& g = *GImGui;
10014
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
10015
if (lock_threshold < 0.0f)
10016
lock_threshold = g.IO.MouseDragThreshold;
10017
if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
10018
if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
10019
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
10020
return g.IO.MousePos - g.IO.MouseClickedPos[button];
10021
return ImVec2(0.0f, 0.0f);
10022
}
10023
10024
void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
10025
{
10026
ImGuiContext& g = *GImGui;
10027
IM_ASSERT(button >= 0 && button < IM_COUNTOF(g.IO.MouseDown));
10028
// NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
10029
g.IO.MouseClickedPos[button] = g.IO.MousePos;
10030
}
10031
10032
// Get desired mouse cursor shape.
10033
// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
10034
// updated during the frame, and locked in EndFrame()/Render().
10035
// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
10036
ImGuiMouseCursor ImGui::GetMouseCursor()
10037
{
10038
ImGuiContext& g = *GImGui;
10039
return g.MouseCursor;
10040
}
10041
10042
// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
10043
// Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may
10044
// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
10045
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
10046
{
10047
ImGuiContext& g = *GImGui;
10048
g.MouseCursor = cursor_type;
10049
}
10050
10051
static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
10052
{
10053
IM_ASSERT(ImGui::IsAliasKey(key));
10054
ImGuiKeyData* key_data = ImGui::GetKeyData(key);
10055
key_data->Down = v;
10056
key_data->AnalogValue = analog_value;
10057
}
10058
10059
// [Internal] Do not use directly
10060
static ImGuiKeyChord GetMergedModsFromKeys()
10061
{
10062
ImGuiKeyChord mods = 0;
10063
if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; }
10064
if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; }
10065
if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; }
10066
if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; }
10067
return mods;
10068
}
10069
10070
static void ImGui::UpdateKeyboardInputs()
10071
{
10072
ImGuiContext& g = *GImGui;
10073
ImGuiIO& io = g.IO;
10074
10075
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10076
io.ClearInputKeys();
10077
10078
// Update aliases
10079
for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
10080
UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
10081
UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
10082
UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
10083
10084
// Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
10085
// - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
10086
// - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
10087
// So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
10088
const ImGuiKeyChord prev_key_mods = io.KeyMods;
10089
io.KeyMods = GetMergedModsFromKeys();
10090
io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
10091
io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
10092
io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
10093
io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
10094
if (prev_key_mods != io.KeyMods)
10095
g.LastKeyModsChangeTime = g.Time;
10096
if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
10097
g.LastKeyModsChangeFromNoneTime = g.Time;
10098
10099
// Clear gamepad data if disabled
10100
if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
10101
for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++)
10102
{
10103
io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false;
10104
io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f;
10105
}
10106
10107
// Update keys
10108
for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
10109
{
10110
ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
10111
key_data->DownDurationPrev = key_data->DownDuration;
10112
key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
10113
if (key_data->DownDuration == 0.0f)
10114
{
10115
if (IsKeyboardKey((ImGuiKey)key))
10116
g.LastKeyboardKeyPressTime = g.Time;
10117
else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
10118
g.LastKeyboardKeyPressTime = g.Time;
10119
}
10120
}
10121
10122
// Update keys/input owner (named keys only): one entry per key
10123
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
10124
{
10125
ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
10126
ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
10127
owner_data->OwnerCurr = owner_data->OwnerNext;
10128
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.
10129
owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
10130
owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
10131
}
10132
10133
// Update key routing (for e.g. shortcuts)
10134
UpdateKeyRoutingTable(&g.KeysRoutingTable);
10135
}
10136
10137
static void ImGui::UpdateMouseInputs()
10138
{
10139
ImGuiContext& g = *GImGui;
10140
ImGuiIO& io = g.IO;
10141
10142
// Mouse Wheel swapping flag
10143
// As a standard behavior holding Shift while using Vertical Mouse Wheel triggers Horizontal scroll instead
10144
// - We avoid doing it on OSX as it the OS input layer handles this already.
10145
// - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
10146
// - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
10147
io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
10148
10149
// Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
10150
if (IsMousePosValid(&io.MousePos))
10151
io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
10152
10153
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
10154
if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
10155
io.MouseDelta = io.MousePos - io.MousePosPrev;
10156
else
10157
io.MouseDelta = ImVec2(0.0f, 0.0f);
10158
10159
// Update stationary timer.
10160
// FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
10161
const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
10162
const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
10163
g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
10164
//IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
10165
10166
// If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
10167
if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
10168
g.NavHighlightItemUnderNav = false;
10169
10170
for (int i = 0; i < IM_COUNTOF(io.MouseDown); i++)
10171
{
10172
io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
10173
io.MouseClickedCount[i] = 0; // Will be filled below
10174
io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
10175
if (io.MouseReleased[i])
10176
io.MouseReleasedTime[i] = g.Time;
10177
io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
10178
io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
10179
if (io.MouseClicked[i])
10180
{
10181
bool is_repeated_click = false;
10182
if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
10183
{
10184
ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10185
if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
10186
is_repeated_click = true;
10187
}
10188
if (is_repeated_click)
10189
io.MouseClickedLastCount[i]++;
10190
else
10191
io.MouseClickedLastCount[i] = 1;
10192
io.MouseClickedTime[i] = g.Time;
10193
io.MouseClickedPos[i] = io.MousePos;
10194
io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
10195
io.MouseDragMaxDistanceSqr[i] = 0.0f;
10196
}
10197
else if (io.MouseDown[i])
10198
{
10199
// Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
10200
float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f;
10201
io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos);
10202
}
10203
10204
// We provide io.MouseDoubleClicked[] as a legacy service
10205
io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
10206
10207
// Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
10208
if (io.MouseClicked[i])
10209
g.NavHighlightItemUnderNav = false;
10210
}
10211
}
10212
10213
static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
10214
{
10215
ImGuiContext& g = *GImGui;
10216
if (window)
10217
g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
10218
else
10219
g.WheelingWindowReleaseTimer = 0.0f;
10220
if (g.WheelingWindow == window)
10221
return;
10222
IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
10223
g.WheelingWindow = window;
10224
g.WheelingWindowRefMousePos = g.IO.MousePos;
10225
if (window == NULL)
10226
{
10227
g.WheelingWindowStartFrame = -1;
10228
g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
10229
}
10230
}
10231
10232
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
10233
{
10234
// For each axis, find window in the hierarchy that may want to use scrolling
10235
ImGuiContext& g = *GImGui;
10236
ImGuiWindow* windows[2] = { NULL, NULL };
10237
for (int axis = 0; axis < 2; axis++)
10238
if (wheel[axis] != 0.0f)
10239
for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
10240
{
10241
// Bubble up into parent window if:
10242
// - a child window doesn't allow any scrolling.
10243
// - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
10244
//// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
10245
const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
10246
const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
10247
//const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
10248
if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
10249
break; // select this window
10250
}
10251
if (windows[0] == NULL && windows[1] == NULL)
10252
return NULL;
10253
10254
// If there's only one window or only one axis then there's no ambiguity
10255
if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
10256
return windows[1] ? windows[1] : windows[0];
10257
10258
// If candidate are different windows we need to decide which one to prioritize
10259
// - First frame: only find a winner if one axis is zero.
10260
// - Subsequent frames: only find a winner when one is more than the other.
10261
if (g.WheelingWindowStartFrame == -1)
10262
g.WheelingWindowStartFrame = g.FrameCount;
10263
if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
10264
{
10265
g.WheelingWindowWheelRemainder = wheel;
10266
return NULL;
10267
}
10268
return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
10269
}
10270
10271
// Called by NewFrame()
10272
void ImGui::UpdateMouseWheel()
10273
{
10274
// Reset the locked window if we move the mouse or after the timer elapses.
10275
// 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)
10276
ImGuiContext& g = *GImGui;
10277
if (g.WheelingWindow != NULL)
10278
{
10279
g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
10280
if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
10281
g.WheelingWindowReleaseTimer = 0.0f;
10282
if (g.WheelingWindowReleaseTimer <= 0.0f)
10283
LockWheelingWindow(NULL, 0.0f);
10284
}
10285
10286
ImVec2 wheel;
10287
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
10288
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
10289
10290
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
10291
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
10292
if (!mouse_window || mouse_window->Collapsed)
10293
return;
10294
10295
// Zoom / Scale window
10296
// FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
10297
if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
10298
{
10299
LockWheelingWindow(mouse_window, wheel.y);
10300
ImGuiWindow* window = mouse_window;
10301
const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
10302
const float scale = new_font_scale / window->FontWindowScale;
10303
window->FontWindowScale = new_font_scale;
10304
if (window == window->RootWindow)
10305
{
10306
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
10307
SetWindowPos(window, window->Pos + offset, 0);
10308
window->Size = ImTrunc(window->Size * scale); // FIXME: Legacy-ish code, call SetWindowSize()?
10309
window->SizeFull = ImTrunc(window->SizeFull * scale);
10310
MarkIniSettingsDirty(window);
10311
}
10312
return;
10313
}
10314
if (g.IO.KeyCtrl)
10315
return;
10316
10317
// Mouse wheel scrolling
10318
// Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
10319
if (g.IO.MouseWheelRequestAxisSwap)
10320
wheel = ImVec2(wheel.y, 0.0f);
10321
10322
// Maintain a rough average of moving magnitude on both axes
10323
// FIXME: should by based on wall clock time rather than frame-counter
10324
g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
10325
g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
10326
10327
// In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
10328
wheel += g.WheelingWindowWheelRemainder;
10329
g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
10330
if (wheel.x == 0.0f && wheel.y == 0.0f)
10331
return;
10332
10333
// Mouse wheel scrolling: find target and apply
10334
// - don't renew lock if axis doesn't apply on the window.
10335
// - select a main axis when both axes are being moved.
10336
if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
10337
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
10338
{
10339
bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
10340
if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
10341
do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
10342
if (do_scroll[ImGuiAxis_X])
10343
{
10344
LockWheelingWindow(window, wheel.x);
10345
float max_step = window->InnerRect.GetWidth() * 0.67f;
10346
float scroll_step = (window->ScrollStepSize.x != 0.0f) ? window->ScrollStepSize.x : ImTrunc(ImMin(2 * window->FontRefSize, max_step));
10347
SetScrollX(window, window->ScrollExpected.x - wheel.x * scroll_step);
10348
g.WheelingWindowScrolledFrame = g.FrameCount;
10349
}
10350
if (do_scroll[ImGuiAxis_Y])
10351
{
10352
LockWheelingWindow(window, wheel.y);
10353
float max_step = window->InnerRect.GetHeight() * 0.67f;
10354
float scroll_step = (window->ScrollStepSize.y != 0.0f) ? window->ScrollStepSize.y : ImTrunc(ImMin(5 * window->FontRefSize, max_step));
10355
SetScrollY(window, window->ScrollExpected.y - wheel.y * scroll_step);
10356
g.WheelingWindowScrolledFrame = g.FrameCount;
10357
}
10358
}
10359
}
10360
10361
void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
10362
{
10363
ImGuiContext& g = *GImGui;
10364
g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
10365
}
10366
10367
void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
10368
{
10369
ImGuiContext& g = *GImGui;
10370
g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
10371
}
10372
10373
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10374
static const char* GetInputSourceName(ImGuiInputSource source)
10375
{
10376
const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
10377
IM_ASSERT(IM_COUNTOF(input_source_names) == ImGuiInputSource_COUNT);
10378
if (source < 0 || source >= ImGuiInputSource_COUNT)
10379
return "Unknown";
10380
return input_source_names[source];
10381
}
10382
static const char* GetMouseSourceName(ImGuiMouseSource source)
10383
{
10384
const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
10385
IM_ASSERT(IM_COUNTOF(mouse_source_names) == ImGuiMouseSource_COUNT);
10386
if (source < 0 || source >= ImGuiMouseSource_COUNT)
10387
return "Unknown";
10388
return mouse_source_names[source];
10389
}
10390
static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
10391
{
10392
ImGuiContext& g = *GImGui;
10393
char buf[5];
10394
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; }
10395
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; }
10396
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; }
10397
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; }
10398
if (e->Type == ImGuiInputEventType_Text) { ImTextCharToUtf8(buf, e->Text.Char); IMGUI_DEBUG_LOG_IO("[io] %s: Text: '%s' (U+%08X)\n", prefix, buf, e->Text.Char); return; }
10399
if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
10400
}
10401
#endif
10402
10403
// Process input queue
10404
// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
10405
// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
10406
// - 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)
10407
void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
10408
{
10409
ImGuiContext& g = *GImGui;
10410
ImGuiIO& io = g.IO;
10411
10412
// Only trickle chars<>key when working with InputText()
10413
// FIXME: InputText() could parse event trail?
10414
// FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
10415
const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
10416
10417
bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
10418
int mouse_button_changed = 0x00;
10419
ImBitArray<ImGuiKey_NamedKey_COUNT> key_changed_mask;
10420
10421
int event_n = 0;
10422
for (; event_n < g.InputEventsQueue.Size; event_n++)
10423
{
10424
ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
10425
if (e->Type == ImGuiInputEventType_MousePos)
10426
{
10427
if (g.IO.WantSetMousePos)
10428
continue;
10429
// Trickling Rule: Stop processing queued events if we already handled a mouse button change
10430
ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
10431
if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
10432
break;
10433
io.MousePos = event_pos;
10434
io.MouseSource = e->MousePos.MouseSource;
10435
mouse_moved = true;
10436
}
10437
else if (e->Type == ImGuiInputEventType_MouseButton)
10438
{
10439
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
10440
const ImGuiMouseButton button = e->MouseButton.Button;
10441
IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
10442
if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
10443
break;
10444
if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
10445
break;
10446
io.MouseDown[button] = e->MouseButton.Down;
10447
io.MouseSource = e->MouseButton.MouseSource;
10448
mouse_button_changed |= (1 << button);
10449
}
10450
else if (e->Type == ImGuiInputEventType_MouseWheel)
10451
{
10452
// Trickling Rule: Stop processing queued events if we got multiple action on the event
10453
if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
10454
break;
10455
io.MouseWheelH += e->MouseWheel.WheelX;
10456
io.MouseWheel += e->MouseWheel.WheelY;
10457
io.MouseSource = e->MouseWheel.MouseSource;
10458
mouse_wheeled = true;
10459
}
10460
else if (e->Type == ImGuiInputEventType_Key)
10461
{
10462
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
10463
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10464
continue;
10465
ImGuiKey key = e->Key.Key;
10466
IM_ASSERT(key != ImGuiKey_None);
10467
ImGuiKeyData* key_data = GetKeyData(key);
10468
const int key_data_index = (int)(key_data - g.IO.KeysData);
10469
if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0))
10470
break;
10471
10472
const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key);
10473
if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))
10474
break;
10475
10476
if (key_data->Down != e->Key.Down) // Analog change only do not trigger this, so it won't block e.g. further mouse pos events testing key_changed.
10477
{
10478
key_changed = true;
10479
key_changed_mask.SetBit(key_data_index);
10480
if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
10481
key_changed_nonchar = true;
10482
}
10483
10484
key_data->Down = e->Key.Down;
10485
key_data->AnalogValue = e->Key.AnalogValue;
10486
}
10487
else if (e->Type == ImGuiInputEventType_Text)
10488
{
10489
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10490
continue;
10491
// Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
10492
if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
10493
break;
10494
if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)
10495
break;
10496
unsigned int c = e->Text.Char;
10497
io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
10498
if (trickle_interleaved_nonchar_keys_and_text)
10499
text_inputted = true;
10500
}
10501
else if (e->Type == ImGuiInputEventType_Focus)
10502
{
10503
// We intentionally overwrite this and process in NewFrame(), in order to give a chance
10504
// to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
10505
const bool focus_lost = !e->AppFocused.Focused;
10506
io.AppFocusLost = focus_lost;
10507
}
10508
else
10509
{
10510
IM_ASSERT(0 && "Unknown event!");
10511
}
10512
}
10513
10514
// Record trail (for domain-specific applications wanting to access a precise trail)
10515
//if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
10516
for (int n = 0; n < event_n; n++)
10517
g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
10518
10519
// [DEBUG]
10520
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10521
if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
10522
for (int n = 0; n < g.InputEventsQueue.Size; n++)
10523
DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
10524
#endif
10525
10526
// Remaining events will be processed on the next frame
10527
if (event_n == g.InputEventsQueue.Size)
10528
g.InputEventsQueue.resize(0);
10529
else
10530
g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
10531
10532
// Clear buttons state when focus is lost
10533
// - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
10534
// - we clear in EndFrame() and not now in order allow application/user code polling this flag
10535
// (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
10536
if (g.IO.AppFocusLost)
10537
{
10538
g.IO.ClearInputKeys();
10539
g.IO.ClearInputMouse();
10540
}
10541
}
10542
10543
ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
10544
{
10545
if (!IsNamedKeyOrMod(key))
10546
return ImGuiKeyOwner_NoOwner;
10547
10548
ImGuiContext& g = *GImGui;
10549
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10550
ImGuiID owner_id = owner_data->OwnerCurr;
10551
10552
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10553
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10554
return ImGuiKeyOwner_NoOwner;
10555
10556
return owner_id;
10557
}
10558
10559
// TestKeyOwner(..., ID) : (owner == None || owner == ID)
10560
// TestKeyOwner(..., None) : (owner == None)
10561
// TestKeyOwner(..., Any) : no owner test
10562
// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
10563
bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
10564
{
10565
if (!IsNamedKeyOrMod(key))
10566
return true;
10567
10568
ImGuiContext& g = *GImGui;
10569
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10570
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10571
return false;
10572
10573
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10574
if (owner_id == ImGuiKeyOwner_Any)
10575
return owner_data->LockThisFrame == false;
10576
10577
// Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
10578
// are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
10579
// Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
10580
if (owner_data->OwnerCurr != owner_id)
10581
{
10582
if (owner_data->LockThisFrame)
10583
return false;
10584
if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
10585
return false;
10586
}
10587
10588
return true;
10589
}
10590
10591
// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
10592
// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
10593
// - SetKeyOwner(..., None) : clears owner
10594
// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
10595
// - SetKeyOwner(..., Any or None, Lock) : set lock
10596
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
10597
{
10598
ImGuiContext& g = *GImGui;
10599
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)
10600
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
10601
//IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
10602
10603
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10604
owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
10605
10606
// We cannot lock by default as it would likely break lots of legacy code.
10607
// In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
10608
owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
10609
owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
10610
}
10611
10612
// Rarely used helper
10613
void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
10614
{
10615
if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
10616
if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
10617
if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
10618
if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
10619
if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
10620
}
10621
10622
// This is more or less equivalent to:
10623
// if (IsItemHovered() || IsItemActive())
10624
// SetKeyOwner(key, GetItemID());
10625
// 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.
10626
// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
10627
// 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.
10628
void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
10629
{
10630
ImGuiContext& g = *GImGui;
10631
ImGuiID id = g.LastItemData.ID;
10632
if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
10633
return;
10634
if ((flags & ImGuiInputFlags_CondMask_) == 0)
10635
flags |= ImGuiInputFlags_CondDefault_;
10636
if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
10637
{
10638
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
10639
SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
10640
}
10641
}
10642
10643
void ImGui::SetItemKeyOwner(ImGuiKey key)
10644
{
10645
SetItemKeyOwner(key, ImGuiInputFlags_None);
10646
}
10647
10648
// This is the only public API until we expose owner_id versions of the API as replacements.
10649
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
10650
{
10651
return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10652
}
10653
10654
// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
10655
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10656
{
10657
ImGuiContext& g = *GImGui;
10658
key_chord = FixupKeyChord(key_chord);
10659
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
10660
if (g.IO.KeyMods != mods)
10661
return false;
10662
10663
// Special storage location for mods
10664
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
10665
if (key == ImGuiKey_None)
10666
key = ConvertSingleModFlagToKey(mods);
10667
if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
10668
return false;
10669
return true;
10670
}
10671
10672
void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10673
{
10674
ImGuiContext& g = *GImGui;
10675
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
10676
g.NextItemData.Shortcut = key_chord;
10677
g.NextItemData.ShortcutFlags = flags;
10678
}
10679
10680
// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
10681
void ImGui::ItemHandleShortcut(ImGuiID id)
10682
{
10683
ImGuiContext& g = *GImGui;
10684
ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
10685
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
10686
10687
if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
10688
return;
10689
if (flags & ImGuiInputFlags_Tooltip)
10690
{
10691
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
10692
g.LastItemData.Shortcut = g.NextItemData.Shortcut;
10693
}
10694
if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
10695
return;
10696
10697
// FIXME: Generalize Activation queue?
10698
g.NavActivateId = id; // Will effectively disable clipping.
10699
g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
10700
//if (g.ActiveId == 0 || g.ActiveId == id)
10701
g.NavActivateDownId = g.NavActivatePressedId = id;
10702
NavHighlightActivated(id);
10703
}
10704
10705
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10706
{
10707
return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
10708
}
10709
10710
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10711
{
10712
ImGuiContext& g = *GImGui;
10713
//IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
10714
10715
// When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
10716
if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
10717
flags |= ImGuiInputFlags_RouteFocused;
10718
10719
// Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
10720
// Effectively makes Shortcut() always input-owner aware.
10721
if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
10722
owner_id = GetRoutingIdFromOwnerId(owner_id);
10723
10724
if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10725
return false;
10726
10727
// Submit route
10728
if (!SetShortcutRouting(key_chord, flags, owner_id))
10729
return false;
10730
10731
// Default repeat behavior for Shortcut()
10732
// So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
10733
if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
10734
flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
10735
10736
if (!IsKeyChordPressed(key_chord, flags, owner_id))
10737
return false;
10738
10739
// Claim mods during the press
10740
SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
10741
10742
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
10743
return true;
10744
}
10745
10746
//-----------------------------------------------------------------------------
10747
// [SECTION] ERROR CHECKING, STATE RECOVERY
10748
//-----------------------------------------------------------------------------
10749
// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
10750
// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10751
// - ErrorCheckNewFrameSanityChecks()
10752
// - ErrorCheckEndFrameSanityChecks()
10753
// - ErrorRecoveryStoreState()
10754
// - ErrorRecoveryTryToRecoverState()
10755
// - ErrorRecoveryTryToRecoverWindowState()
10756
// - ErrorLog()
10757
//-----------------------------------------------------------------------------
10758
10759
// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
10760
// Called by IMGUI_CHECKVERSION().
10761
// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
10762
// If this triggers you have mismatched headers and compiled code versions.
10763
// - It could be because of a build issue (using new headers with old compiled code)
10764
// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
10765
// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
10766
// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
10767
// Otherwise it is possible that different compilation units would see different structure layout.
10768
// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
10769
bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
10770
{
10771
bool error = false;
10772
if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
10773
if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
10774
if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
10775
if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
10776
if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
10777
if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
10778
if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
10779
return !error;
10780
}
10781
10782
// Until 1.89 (August 2022, IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos()/SetCursorScreenPos()
10783
// to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing
10784
// windows, table column contents size used for auto-resizing columns, group size).
10785
// This was causing issues and ambiguities and we needed to retire that.
10786
// From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED.
10787
//
10788
// Previously this would make the window content size ~200x200:
10789
// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK ANYMORE
10790
// Instead, please submit an item:
10791
// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
10792
// Alternative:
10793
// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
10794
//
10795
// The assert below detects when the _last_ call in a window was a SetCursorPos() not followed by an Item,
10796
// and with a position that would grow the parent contents size.
10797
//
10798
// Advanced:
10799
// - For reference, old logic was causing issues because it meant that SetCursorScreenPos(GetCursorScreenPos())
10800
// had a side-effect on layout! In particular this caused problem to compute group boundaries.
10801
// e.g. BeginGroup() + SomeItem() + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() would cause the
10802
// group to be taller because auto-sizing generally adds padding on bottom and right side.
10803
// - While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect.
10804
// Using vertical alignment patterns would frequently trigger this sorts of issue.
10805
// - See https://github.com/ocornut/imgui/issues/5548 for more details.
10806
void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10807
{
10808
ImGuiContext& g = *GImGui;
10809
ImGuiWindow* window = g.CurrentWindow;
10810
IM_ASSERT(window->DC.IsSetPos);
10811
window->DC.IsSetPos = false;
10812
if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
10813
return;
10814
if (window->SkipItems)
10815
return;
10816
IM_ASSERT_USER_ERROR(0, "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries.\nPlease submit an item e.g. Dummy() afterwards in order to grow window/parent boundaries.");
10817
10818
// For reference, the old behavior was essentially:
10819
//window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
10820
}
10821
10822
static void ImGui::ErrorCheckNewFrameSanityChecks()
10823
{
10824
ImGuiContext& g = *GImGui;
10825
10826
// Check user IM_ASSERT macro
10827
// (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
10828
// If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
10829
// This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
10830
// #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
10831
// #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
10832
if (true) IM_ASSERT(1); else IM_ASSERT(0);
10833
10834
// Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
10835
// Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
10836
#ifdef __EMSCRIPTEN__
10837
if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
10838
g.IO.DeltaTime = 0.00001f;
10839
#endif
10840
10841
// Check user data
10842
// (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)
10843
IM_ASSERT(g.Initialized);
10844
IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
10845
IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
10846
IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
10847
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
10848
IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
10849
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
10850
IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting!");
10851
IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f && "Invalid style setting!"); // Required otherwise cannot resize from borders.
10852
IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
10853
IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
10854
IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes);
10855
10856
// Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
10857
if (g.IO.ConfigErrorRecovery)
10858
IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
10859
10860
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
10861
if (g.IO.FontGlobalScale > 1.0f)
10862
IM_ASSERT(g.Style.FontScaleMain == 1.0f && "Since 1.92: use style.FontScaleMain instead of g.IO.FontGlobalScale!");
10863
10864
// Remap legacy names
10865
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
10866
{
10867
g.IO.ConfigNavMoveSetMousePos = true;
10868
g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
10869
}
10870
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
10871
{
10872
g.IO.ConfigNavCaptureKeyboard = false;
10873
g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
10874
}
10875
10876
// Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
10877
if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
10878
g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
10879
if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
10880
g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };
10881
#endif
10882
}
10883
10884
static void ImGui::ErrorCheckEndFrameSanityChecks()
10885
{
10886
// Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
10887
// One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
10888
// It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
10889
// send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
10890
// We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
10891
// while still correctly asserting on mid-frame key press events.
10892
ImGuiContext& g = *GImGui;
10893
const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
10894
IM_UNUSED(g);
10895
IM_UNUSED(key_mods);
10896
IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
10897
IM_UNUSED(key_mods);
10898
10899
IM_ASSERT(g.CurrentWindowStack.Size == 1);
10900
IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
10901
}
10902
10903
// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
10904
void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
10905
{
10906
ImGuiContext& g = *GImGui;
10907
state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
10908
state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
10909
state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
10910
state_out->SizeOfColorStack = (short)g.ColorStack.Size;
10911
state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
10912
state_out->SizeOfFontStack = (short)g.FontStack.Size;
10913
state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
10914
state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
10915
state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
10916
state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
10917
state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
10918
}
10919
10920
// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
10921
// Called by e.g. EndFrame() but may be called for manual recovery.
10922
// Attempt to recover full window stack.
10923
void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
10924
{
10925
// PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
10926
ImGuiContext& g = *GImGui;
10927
while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
10928
{
10929
// Recap:
10930
// - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
10931
// - Always call a matching End() for each Begin() call, regardless of its return value!
10932
// - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
10933
// - We will fix that in a future major update.
10934
ImGuiWindow* window = g.CurrentWindow;
10935
if (window->Flags & ImGuiWindowFlags_ChildWindow)
10936
{
10937
if (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
10938
{
10939
IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
10940
EndTable();
10941
}
10942
else
10943
{
10944
IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
10945
EndChild();
10946
}
10947
}
10948
else
10949
{
10950
IM_ASSERT_USER_ERROR(0, "Missing End()");
10951
End();
10952
}
10953
}
10954
if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
10955
ErrorRecoveryTryToRecoverWindowState(state_in);
10956
}
10957
10958
// Called by e.g. End() but may be called for manual recovery.
10959
// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
10960
// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
10961
void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
10962
{
10963
ImGuiContext& g = *GImGui;
10964
10965
while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
10966
{
10967
IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
10968
EndTable();
10969
}
10970
10971
ImGuiWindow* window = g.CurrentWindow;
10972
10973
// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
10974
while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
10975
{
10976
IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
10977
EndTabBar();
10978
}
10979
while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
10980
{
10981
IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
10982
EndMultiSelect();
10983
}
10984
if (window->DC.MenuBarAppending) //-V1044
10985
{
10986
IM_ASSERT_USER_ERROR(0, "Missing EndMenuBar()");
10987
EndMenuBar();
10988
}
10989
while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
10990
{
10991
IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
10992
TreePop();
10993
}
10994
while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
10995
{
10996
IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
10997
EndGroup();
10998
}
10999
IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
11000
while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
11001
{
11002
IM_ASSERT_USER_ERROR(0, "Missing PopID()");
11003
PopID();
11004
}
11005
while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
11006
{
11007
IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
11008
if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
11009
EndDisabled();
11010
else
11011
{
11012
EndDisabledOverrideReenable();
11013
g.CurrentWindowStack.back().DisabledOverrideReenable = false;
11014
}
11015
}
11016
IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
11017
while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
11018
{
11019
IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
11020
PopStyleColor();
11021
}
11022
while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
11023
{
11024
IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
11025
PopItemFlag();
11026
}
11027
while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
11028
{
11029
IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
11030
PopStyleVar();
11031
}
11032
while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
11033
{
11034
IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
11035
PopFont();
11036
}
11037
while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
11038
{
11039
IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
11040
PopFocusScope();
11041
}
11042
//IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
11043
}
11044
11045
bool ImGui::ErrorLog(const char* msg)
11046
{
11047
ImGuiContext& g = *GImGui;
11048
11049
// Output to debug log
11050
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11051
ImGuiWindow* window = g.CurrentWindow;
11052
11053
if (g.IO.ConfigErrorRecoveryEnableDebugLog)
11054
{
11055
if (g.ErrorFirst)
11056
IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
11057
g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
11058
IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
11059
}
11060
g.ErrorFirst = false;
11061
11062
// Output to tooltip
11063
if (g.IO.ConfigErrorRecoveryEnableTooltip)
11064
{
11065
if (g.WithinFrameScope && BeginErrorTooltip())
11066
{
11067
if (g.ErrorCountCurrentFrame < 20)
11068
{
11069
Text("In window '%s': %s", window ? window->Name : "NULL", msg);
11070
if (window && (!window->IsFallbackWindow || window->WasActive))
11071
GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
11072
}
11073
if (g.ErrorCountCurrentFrame == 20)
11074
Text("(and more errors)");
11075
// EndFrame() will amend debug buttons to this window, after all errors have been submitted.
11076
EndErrorTooltip();
11077
}
11078
g.ErrorCountCurrentFrame++;
11079
}
11080
#endif
11081
11082
// Output to callback
11083
if (g.ErrorCallback != NULL)
11084
g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
11085
11086
// Return whether we should assert
11087
return g.IO.ConfigErrorRecoveryEnableAssert;
11088
}
11089
11090
void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
11091
{
11092
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11093
ImGuiContext& g = *GImGui;
11094
if (g.DebugDrawIdConflictsId != 0 && g.IO.KeyCtrl == false)
11095
g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
11096
if (g.DebugDrawIdConflictsId != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
11097
{
11098
Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
11099
BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
11100
BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
11101
//BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused.
11102
BulletText("Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.");
11103
Separator();
11104
if (g.IO.ConfigDebugHighlightIdConflictsShowItemPicker)
11105
{
11106
Text("(Hold Ctrl to: use ");
11107
SameLine(0.0f, 0.0f);
11108
if (SmallButton("Item Picker"))
11109
DebugStartItemPicker();
11110
SameLine(0.0f, 0.0f);
11111
Text(" to break in item call-stack, or ");
11112
}
11113
else
11114
{
11115
Text("(Hold Ctrl to: ");
11116
}
11117
SameLine(0.0f, 0.0f);
11118
TextLinkOpenURL("read FAQ \"About ID Stack System\"", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
11119
SameLine(0.0f, 0.0f);
11120
Text(")");
11121
EndErrorTooltip();
11122
}
11123
11124
if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
11125
{
11126
Separator();
11127
Text("(Hold Ctrl to: ");
11128
SameLine(0.0f, 0.0f);
11129
if (SmallButton("Enable Asserts"))
11130
g.IO.ConfigErrorRecoveryEnableAssert = true;
11131
//SameLine();
11132
//if (SmallButton("Hide Error Tooltips"))
11133
// g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
11134
SameLine(0, 0);
11135
Text(")");
11136
EndErrorTooltip();
11137
}
11138
#endif
11139
}
11140
11141
// Pseudo-tooltip. Follow mouse until Ctrl is held. When Ctrl is held we lock position, allowing to click it.
11142
bool ImGui::BeginErrorTooltip()
11143
{
11144
ImGuiContext& g = *GImGui;
11145
ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
11146
const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
11147
PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
11148
if (use_locked_pos)
11149
SetNextWindowPos(g.ErrorTooltipLockedPos);
11150
bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
11151
PopStyleColor();
11152
if (is_visible && g.CurrentWindow->BeginCount == 1)
11153
{
11154
SeparatorText("MESSAGE FROM DEAR IMGUI");
11155
BringWindowToDisplayFront(g.CurrentWindow);
11156
BringWindowToFocusFront(g.CurrentWindow);
11157
g.ErrorTooltipLockedPos = GetWindowPos();
11158
}
11159
else if (!is_visible)
11160
{
11161
End();
11162
}
11163
return is_visible;
11164
}
11165
11166
void ImGui::EndErrorTooltip()
11167
{
11168
End();
11169
}
11170
11171
//-----------------------------------------------------------------------------
11172
// [SECTION] ITEM SUBMISSION
11173
//-----------------------------------------------------------------------------
11174
// - KeepAliveID()
11175
// - ItemAdd()
11176
//-----------------------------------------------------------------------------
11177
11178
// 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().
11179
void ImGui::KeepAliveID(ImGuiID id)
11180
{
11181
ImGuiContext& g = *GImGui;
11182
if (g.ActiveId == id)
11183
g.ActiveIdIsAlive = id;
11184
if (g.DeactivatedItemData.ID == id)
11185
g.DeactivatedItemData.IsAlive = true;
11186
}
11187
11188
// Declare item bounding box for clipping and interaction.
11189
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
11190
// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
11191
// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
11192
IM_MSVC_RUNTIME_CHECKS_OFF
11193
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
11194
{
11195
ImGuiContext& g = *GImGui;
11196
ImGuiWindow* window = g.CurrentWindow;
11197
11198
// Set item data
11199
// (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
11200
g.LastItemData.ID = id;
11201
g.LastItemData.Rect = bb;
11202
g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
11203
g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
11204
g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
11205
// Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
11206
11207
if (id != 0)
11208
{
11209
KeepAliveID(id);
11210
11211
// Directional navigation processing
11212
// Runs prior to clipping early-out
11213
// (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
11214
// (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
11215
// unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
11216
// thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
11217
// We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
11218
// to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
11219
// We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
11220
// If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
11221
if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
11222
{
11223
// FIXME-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
11224
window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
11225
if (g.NavId == id || g.NavAnyRequest)
11226
if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
11227
if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
11228
NavProcessItem();
11229
}
11230
11231
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
11232
ItemHandleShortcut(id);
11233
}
11234
11235
// Lightweight clear of SetNextItemXXX data.
11236
g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
11237
g.NextItemData.ItemFlags = ImGuiItemFlags_None;
11238
11239
#ifdef IMGUI_ENABLE_TEST_ENGINE
11240
if (id != 0)
11241
IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
11242
#endif
11243
11244
// Clipping test
11245
// (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
11246
// g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
11247
const bool is_rect_visible = bb.Overlaps(window->ClipRect);
11248
if (!is_rect_visible)
11249
if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
11250
if (!g.ItemUnclipByLog)
11251
return false;
11252
11253
// [DEBUG]
11254
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11255
if (id != 0)
11256
{
11257
if (id == g.DebugLocateId)
11258
DebugLocateItemResolveWithLastItem();
11259
11260
// [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
11261
// Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
11262
// READ THE FAQ: https://dearimgui.com/faq
11263
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!");
11264
11265
// [DEBUG] Highlight all conflicts WITHOUT needing to hover. THIS WILL SLOW DOWN DEAR IMGUI. DON'T KEEP ACTIVATED.
11266
// This will only work for items submitted with ItemAdd(). Some very rare/odd/unrecommended code patterns are calling ButtonBehavior() without ItemAdd().
11267
#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
11268
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowDuplicateId) == 0)
11269
{
11270
int* p_alive = g.DebugDrawIdConflictsAliveCount.GetIntRef(id, -1); // Could halve lookups if we knew ImGuiStorage can store 64-bit, or by storing FrameCount as 30-bits + highlight as 2-bits. But the point is that we should not pretend that this is fast.
11271
int* p_highlight = g.DebugDrawIdConflictsHighlightSet.GetIntRef(id, -1);
11272
if (*p_alive == g.FrameCount)
11273
*p_highlight = g.FrameCount;
11274
*p_alive = g.FrameCount;
11275
if (*p_highlight >= g.FrameCount - 1)
11276
window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
11277
}
11278
#endif
11279
}
11280
//if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
11281
//if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
11282
// window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
11283
#endif
11284
11285
if (id != 0 && g.DeactivatedItemData.ID == id)
11286
g.DeactivatedItemData.ElapseFrame = g.FrameCount;
11287
11288
// We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
11289
if (is_rect_visible)
11290
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
11291
if (IsMouseHoveringRect(bb.Min, bb.Max))
11292
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
11293
return true;
11294
}
11295
IM_MSVC_RUNTIME_CHECKS_RESTORE
11296
11297
//-----------------------------------------------------------------------------
11298
// [SECTION] LAYOUT
11299
//-----------------------------------------------------------------------------
11300
// - ItemSize()
11301
// - SameLine()
11302
// - GetCursorScreenPos()
11303
// - SetCursorScreenPos()
11304
// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
11305
// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
11306
// - GetCursorStartPos()
11307
// - Indent()
11308
// - Unindent()
11309
// - SetNextItemWidth()
11310
// - PushItemWidth()
11311
// - PushMultiItemsWidths()
11312
// - PopItemWidth()
11313
// - CalcItemWidth()
11314
// - CalcItemSize()
11315
// - GetTextLineHeight()
11316
// - GetTextLineHeightWithSpacing()
11317
// - GetFrameHeight()
11318
// - GetFrameHeightWithSpacing()
11319
// - GetContentRegionMax()
11320
// - GetContentRegionAvail(),
11321
// - BeginGroup()
11322
// - EndGroup()
11323
// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
11324
//-----------------------------------------------------------------------------
11325
11326
// Advance cursor given item size for layout.
11327
// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
11328
// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
11329
// THIS IS IN THE PERFORMANCE CRITICAL PATH.
11330
IM_MSVC_RUNTIME_CHECKS_OFF
11331
void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
11332
{
11333
ImGuiContext& g = *GImGui;
11334
ImGuiWindow* window = g.CurrentWindow;
11335
if (window->SkipItems)
11336
return;
11337
11338
// We increase the height in this function to accommodate for baseline offset.
11339
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
11340
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
11341
const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
11342
11343
const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
11344
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);
11345
11346
// Always align ourselves on pixel boundaries
11347
//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]
11348
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
11349
window->DC.CursorPosPrevLine.y = line_y1;
11350
window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
11351
window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
11352
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
11353
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
11354
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11355
11356
window->DC.PrevLineSize.y = line_height;
11357
window->DC.CurrLineSize.y = 0.0f;
11358
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
11359
window->DC.CurrLineTextBaseOffset = 0.0f;
11360
window->DC.IsSameLine = window->DC.IsSetPos = false;
11361
11362
// Horizontal layout mode
11363
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11364
SameLine();
11365
}
11366
IM_MSVC_RUNTIME_CHECKS_RESTORE
11367
11368
// Gets back to previous line and continue with horizontal layout
11369
// offset_from_start_x == 0 : follow right after previous item
11370
// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
11371
// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
11372
// spacing_w >= 0 : enforce spacing amount
11373
void ImGui::SameLine(float offset_from_start_x, float spacing_w)
11374
{
11375
ImGuiContext& g = *GImGui;
11376
ImGuiWindow* window = g.CurrentWindow;
11377
if (window->SkipItems)
11378
return;
11379
11380
if (offset_from_start_x != 0.0f)
11381
{
11382
if (spacing_w < 0.0f)
11383
spacing_w = 0.0f;
11384
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
11385
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11386
}
11387
else
11388
{
11389
if (spacing_w < 0.0f)
11390
spacing_w = g.Style.ItemSpacing.x;
11391
window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
11392
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11393
}
11394
window->DC.CurrLineSize = window->DC.PrevLineSize;
11395
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11396
window->DC.IsSameLine = true;
11397
}
11398
11399
ImVec2 ImGui::GetCursorScreenPos()
11400
{
11401
ImGuiWindow* window = GetCurrentWindowRead();
11402
return window->DC.CursorPos;
11403
}
11404
11405
void ImGui::SetCursorScreenPos(const ImVec2& pos)
11406
{
11407
ImGuiWindow* window = GetCurrentWindow();
11408
window->DC.CursorPos = pos;
11409
//window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11410
window->DC.IsSetPos = true;
11411
}
11412
11413
// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
11414
// 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'.
11415
ImVec2 ImGui::GetCursorPos()
11416
{
11417
ImGuiWindow* window = GetCurrentWindowRead();
11418
return window->DC.CursorPos - window->Pos + window->Scroll;
11419
}
11420
11421
float ImGui::GetCursorPosX()
11422
{
11423
ImGuiWindow* window = GetCurrentWindowRead();
11424
return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
11425
}
11426
11427
float ImGui::GetCursorPosY()
11428
{
11429
ImGuiWindow* window = GetCurrentWindowRead();
11430
return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
11431
}
11432
11433
void ImGui::SetCursorPos(const ImVec2& local_pos)
11434
{
11435
ImGuiWindow* window = GetCurrentWindow();
11436
window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
11437
//window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11438
window->DC.IsSetPos = true;
11439
}
11440
11441
void ImGui::SetCursorPosX(float x)
11442
{
11443
ImGuiWindow* window = GetCurrentWindow();
11444
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
11445
//window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
11446
window->DC.IsSetPos = true;
11447
}
11448
11449
void ImGui::SetCursorPosY(float y)
11450
{
11451
ImGuiWindow* window = GetCurrentWindow();
11452
window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
11453
//window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
11454
window->DC.IsSetPos = true;
11455
}
11456
11457
ImVec2 ImGui::GetCursorStartPos()
11458
{
11459
ImGuiWindow* window = GetCurrentWindowRead();
11460
return window->DC.CursorStartPos - window->Pos;
11461
}
11462
11463
void ImGui::Indent(float indent_w)
11464
{
11465
ImGuiContext& g = *GImGui;
11466
ImGuiWindow* window = GetCurrentWindow();
11467
window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11468
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11469
}
11470
11471
void ImGui::Unindent(float indent_w)
11472
{
11473
ImGuiContext& g = *GImGui;
11474
ImGuiWindow* window = GetCurrentWindow();
11475
window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11476
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11477
}
11478
11479
// Affect large frame+labels widgets only.
11480
void ImGui::SetNextItemWidth(float item_width)
11481
{
11482
ImGuiContext& g = *GImGui;
11483
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
11484
g.NextItemData.Width = item_width;
11485
}
11486
11487
// FIXME: Remove the == 0.0f behavior?
11488
void ImGui::PushItemWidth(float item_width)
11489
{
11490
ImGuiContext& g = *GImGui;
11491
ImGuiWindow* window = g.CurrentWindow;
11492
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11493
window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
11494
g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11495
}
11496
11497
void ImGui::PushMultiItemsWidths(int components, float w_full)
11498
{
11499
ImGuiContext& g = *GImGui;
11500
ImGuiWindow* window = g.CurrentWindow;
11501
IM_ASSERT(components > 0);
11502
const ImGuiStyle& style = g.Style;
11503
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11504
float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
11505
float prev_split = w_items;
11506
for (int i = components - 1; i > 0; i--)
11507
{
11508
float next_split = IM_TRUNC(w_items * i / components);
11509
window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
11510
prev_split = next_split;
11511
}
11512
window->DC.ItemWidth = ImMax(prev_split, 1.0f);
11513
g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11514
}
11515
11516
void ImGui::PopItemWidth()
11517
{
11518
ImGuiContext& g = *GImGui;
11519
ImGuiWindow* window = g.CurrentWindow;
11520
if (window->DC.ItemWidthStack.Size <= 0)
11521
{
11522
IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
11523
return;
11524
}
11525
window->DC.ItemWidth = window->DC.ItemWidthStack.back();
11526
window->DC.ItemWidthStack.pop_back();
11527
}
11528
11529
// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
11530
// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
11531
float ImGui::CalcItemWidth()
11532
{
11533
ImGuiContext& g = *GImGui;
11534
ImGuiWindow* window = g.CurrentWindow;
11535
float w;
11536
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
11537
w = g.NextItemData.Width;
11538
else
11539
w = window->DC.ItemWidth;
11540
if (w < 0.0f)
11541
{
11542
float region_avail_x = GetContentRegionAvail().x;
11543
w = ImMax(1.0f, region_avail_x + w);
11544
}
11545
w = IM_TRUNC(w);
11546
return w;
11547
}
11548
11549
// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
11550
// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
11551
// Note that only CalcItemWidth() is publicly exposed.
11552
// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
11553
ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
11554
{
11555
ImVec2 avail;
11556
if (size.x < 0.0f || size.y < 0.0f)
11557
avail = GetContentRegionAvail();
11558
11559
if (size.x == 0.0f)
11560
size.x = default_w;
11561
else if (size.x < 0.0f)
11562
size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting
11563
11564
if (size.y == 0.0f)
11565
size.y = default_h;
11566
else if (size.y < 0.0f)
11567
size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting
11568
11569
return size;
11570
}
11571
11572
float ImGui::GetTextLineHeight()
11573
{
11574
ImGuiContext& g = *GImGui;
11575
return g.FontSize;
11576
}
11577
11578
float ImGui::GetTextLineHeightWithSpacing()
11579
{
11580
ImGuiContext& g = *GImGui;
11581
return g.FontSize + g.Style.ItemSpacing.y;
11582
}
11583
11584
float ImGui::GetFrameHeight()
11585
{
11586
ImGuiContext& g = *GImGui;
11587
return g.FontSize + g.Style.FramePadding.y * 2.0f;
11588
}
11589
11590
float ImGui::GetFrameHeightWithSpacing()
11591
{
11592
ImGuiContext& g = *GImGui;
11593
return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
11594
}
11595
11596
ImVec2 ImGui::GetContentRegionAvail()
11597
{
11598
ImGuiContext& g = *GImGui;
11599
ImGuiWindow* window = g.CurrentWindow;
11600
ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11601
return mx - window->DC.CursorPos;
11602
}
11603
11604
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11605
11606
// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
11607
// They are bizarre local-coordinates which don't play well with scrolling.
11608
ImVec2 ImGui::GetContentRegionMax()
11609
{
11610
return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
11611
}
11612
11613
ImVec2 ImGui::GetWindowContentRegionMin()
11614
{
11615
ImGuiWindow* window = GImGui->CurrentWindow;
11616
return window->ContentRegionRect.Min - window->Pos;
11617
}
11618
11619
ImVec2 ImGui::GetWindowContentRegionMax()
11620
{
11621
ImGuiWindow* window = GImGui->CurrentWindow;
11622
return window->ContentRegionRect.Max - window->Pos;
11623
}
11624
#endif
11625
11626
// 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.)
11627
// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
11628
// FIXME-OPT: Could we safely early out on ->SkipItems?
11629
void ImGui::BeginGroup()
11630
{
11631
ImGuiContext& g = *GImGui;
11632
ImGuiWindow* window = g.CurrentWindow;
11633
11634
g.GroupStack.resize(g.GroupStack.Size + 1);
11635
ImGuiGroupData& group_data = g.GroupStack.back();
11636
group_data.WindowID = window->ID;
11637
group_data.BackupCursorPos = window->DC.CursorPos;
11638
group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
11639
group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
11640
group_data.BackupIndent = window->DC.Indent;
11641
group_data.BackupGroupOffset = window->DC.GroupOffset;
11642
group_data.BackupCurrLineSize = window->DC.CurrLineSize;
11643
group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
11644
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
11645
group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
11646
group_data.BackupIsSameLine = window->DC.IsSameLine;
11647
group_data.BackupActiveIdHasBeenEditedThisFrame = g.ActiveIdHasBeenEditedThisFrame;
11648
group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive;
11649
group_data.EmitItem = true;
11650
11651
window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
11652
window->DC.Indent = window->DC.GroupOffset;
11653
window->DC.CursorMaxPos = window->DC.CursorPos;
11654
window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
11655
if (g.LogEnabled)
11656
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11657
}
11658
11659
void ImGui::EndGroup()
11660
{
11661
ImGuiContext& g = *GImGui;
11662
ImGuiWindow* window = g.CurrentWindow;
11663
IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
11664
11665
ImGuiGroupData& group_data = g.GroupStack.back();
11666
IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
11667
11668
if (window->DC.IsSetPos)
11669
ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
11670
11671
// Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
11672
ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));
11673
window->DC.CursorPos = group_data.BackupCursorPos;
11674
window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
11675
window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);
11676
window->DC.Indent = group_data.BackupIndent;
11677
window->DC.GroupOffset = group_data.BackupGroupOffset;
11678
window->DC.CurrLineSize = group_data.BackupCurrLineSize;
11679
window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
11680
window->DC.IsSameLine = group_data.BackupIsSameLine;
11681
if (g.LogEnabled)
11682
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11683
11684
if (!group_data.EmitItem)
11685
{
11686
g.GroupStack.pop_back();
11687
return;
11688
}
11689
11690
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.
11691
ItemSize(group_bb.GetSize());
11692
ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
11693
11694
// 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.
11695
// 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.
11696
// Also if you grep for LastItemId you'll notice it is only used in that context.
11697
// (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.)
11698
const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
11699
const bool group_contains_deactivated_id = (group_data.BackupDeactivatedIdIsAlive == false) && (g.DeactivatedItemData.IsAlive == true);
11700
if (group_contains_curr_active_id)
11701
g.LastItemData.ID = g.ActiveId;
11702
else if (group_contains_deactivated_id)
11703
g.LastItemData.ID = g.DeactivatedItemData.ID;
11704
g.LastItemData.Rect = group_bb;
11705
11706
// Forward Hovered flag
11707
const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
11708
if (group_contains_curr_hovered_id)
11709
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
11710
11711
// Forward Edited flag
11712
if (g.ActiveIdHasBeenEditedThisFrame && !group_data.BackupActiveIdHasBeenEditedThisFrame)
11713
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
11714
11715
// Forward Deactivated flag
11716
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
11717
if (group_contains_deactivated_id)
11718
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
11719
11720
g.GroupStack.pop_back();
11721
if (g.DebugShowGroupRects)
11722
window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
11723
}
11724
11725
11726
//-----------------------------------------------------------------------------
11727
// [SECTION] SCROLLING
11728
//-----------------------------------------------------------------------------
11729
11730
// Helper to snap on edges when aiming at an item very close to the edge,
11731
// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
11732
// When we refactor the scrolling API this may be configurable with a flag?
11733
// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
11734
static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
11735
{
11736
if (target <= snap_min + snap_threshold)
11737
return ImLerp(snap_min, target, center_ratio);
11738
if (target >= snap_max - snap_threshold)
11739
return ImLerp(target, snap_max, center_ratio);
11740
return target;
11741
}
11742
11743
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
11744
{
11745
ImVec2 scroll = window->Scroll;
11746
ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
11747
for (int axis = 0; axis < 2; axis++)
11748
{
11749
if (window->ScrollTarget[axis] < FLT_MAX)
11750
{
11751
float center_ratio = window->ScrollTargetCenterRatio[axis];
11752
float scroll_target = window->ScrollTarget[axis];
11753
if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
11754
{
11755
float snap_min = 0.0f;
11756
float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
11757
scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
11758
}
11759
window->ScrollExpected[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
11760
}
11761
11762
// Based on https://github.com/ocornut/imgui/pull/7348
11763
// Instead use "Scroll" value in the window, all setters that sets the scroll absolutely now points to
11764
// "ScrollExpected" Here, we take from ScrollTarget (from some functions like ScrollHere + mouse wheel) to set
11765
// the ScrollExpected value Also, Scroll var in window is processed to meet ScrollExpected Value
11766
//
11767
// The formula is pretty simple to generate a smooth scrolling that can be tweaked just by one float value.
11768
//
11769
// The Float is "ImGuiStyleVar_ScrollSmooth". Can be set on the style or via PushStyleVar.
11770
// A Value of 1.0f is just inmediate (transported from ScrollExpected to Scroll).
11771
// A Value higher of 1.0f will make the scrolling smoother.
11772
//
11773
// The ScrollExpected is also clamped (as previously the "Scroll" value) from 0 to sScrollMax
11774
//
11775
// The approach is frame bounded and not time bounded.
11776
// It should be prefereable use a time bounded approach but this is pretty simple so we don't need to add extra
11777
// vars to save a scrolling "start" time to have a delta / deal with posible increments during the scrolling
11778
// itself (restar timer) Anyway it should not be complicated to add but this approach is small, simple, can be
11779
// user or not and works pretty well
11780
//
11781
window->ScrollExpected[axis] = ImRound64(ImMax(window->ScrollExpected[axis], 0.0f));
11782
if (!window->Collapsed && !window->SkipItems)
11783
window->ScrollExpected[axis] = ImMin(window->ScrollExpected[axis], window->ScrollMax[axis]);
11784
ImGuiContext& g = *GImGui;
11785
ImGuiStyle& style = g.Style;
11786
if (scroll[axis] != window->ScrollExpected[axis])
11787
{
11788
const float multiplier = GImGui->IO.DeltaTime / (1.0f / ImMax(GImGui->IO.Framerate, 1.0f));
11789
const float diff = window->ScrollExpected[axis] - scroll[axis];
11790
if (diff > 0)
11791
scroll[axis] += ImMin(diff, (diff / (style.ScrollSmooth * multiplier)));
11792
else
11793
scroll[axis] -= ImMin(-diff, (-diff / (style.ScrollSmooth * multiplier)));
11794
11795
scroll[axis] = (window->Appearing || g.ScrollbarHeld & 1) ? window->ScrollExpected[axis] : scroll[axis];
11796
}
11797
}
11798
return scroll;
11799
}
11800
11801
void ImGui::ScrollToItem(ImGuiScrollFlags flags)
11802
{
11803
ImGuiContext& g = *GImGui;
11804
ImGuiWindow* window = g.CurrentWindow;
11805
ScrollToRectEx(window, g.LastItemData.NavRect, flags);
11806
}
11807
11808
void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11809
{
11810
ScrollToRectEx(window, item_rect, flags);
11811
}
11812
11813
// Scroll to keep newly navigated item fully into view
11814
ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11815
{
11816
ImGuiContext& g = *GImGui;
11817
ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
11818
scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
11819
scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
11820
//GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
11821
//GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
11822
11823
// Check that only one behavior is selected per axis
11824
IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
11825
IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
11826
11827
// Defaults
11828
ImGuiScrollFlags in_flags = flags;
11829
if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
11830
flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
11831
if ((flags & ImGuiScrollFlags_MaskY_) == 0)
11832
flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
11833
11834
const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
11835
const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
11836
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;
11837
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;
11838
11839
if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
11840
{
11841
if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
11842
SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
11843
else if (item_rect.Max.x >= scroll_rect.Max.x)
11844
SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
11845
}
11846
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
11847
{
11848
if (can_be_fully_visible_x)
11849
SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
11850
else
11851
SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
11852
}
11853
11854
if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
11855
{
11856
if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
11857
SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
11858
else if (item_rect.Max.y >= scroll_rect.Max.y)
11859
SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
11860
}
11861
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
11862
{
11863
if (can_be_fully_visible_y)
11864
SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
11865
else
11866
SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
11867
}
11868
11869
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
11870
ImVec2 delta_scroll = next_scroll - window->Scroll;
11871
11872
// Also scroll parent window to keep us into view if necessary
11873
if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
11874
{
11875
// FIXME-SCROLL: May be an option?
11876
if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
11877
in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
11878
if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
11879
in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
11880
delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
11881
}
11882
11883
return delta_scroll;
11884
}
11885
11886
float ImGui::GetScrollX()
11887
{
11888
ImGuiWindow* window = GImGui->CurrentWindow;
11889
return window->ScrollExpected.x;
11890
}
11891
11892
float ImGui::GetScrollY()
11893
{
11894
ImGuiWindow* window = GImGui->CurrentWindow;
11895
return window->ScrollExpected.y;
11896
}
11897
11898
float ImGui::GetScrollMaxX()
11899
{
11900
ImGuiWindow* window = GImGui->CurrentWindow;
11901
return window->ScrollMax.x;
11902
}
11903
11904
float ImGui::GetScrollMaxY()
11905
{
11906
ImGuiWindow* window = GImGui->CurrentWindow;
11907
return window->ScrollMax.y;
11908
}
11909
11910
void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
11911
{
11912
window->ScrollTarget.x = scroll_x;
11913
window->ScrollTargetCenterRatio.x = 0.0f;
11914
window->ScrollTargetEdgeSnapDist.x = 0.0f;
11915
}
11916
11917
void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
11918
{
11919
window->ScrollTarget.y = scroll_y;
11920
window->ScrollTargetCenterRatio.y = 0.0f;
11921
window->ScrollTargetEdgeSnapDist.y = 0.0f;
11922
}
11923
11924
void ImGui::SetScrollX(float scroll_x)
11925
{
11926
ImGuiContext& g = *GImGui;
11927
SetScrollX(g.CurrentWindow, scroll_x);
11928
}
11929
11930
void ImGui::SetScrollY(float scroll_y)
11931
{
11932
ImGuiContext& g = *GImGui;
11933
SetScrollY(g.CurrentWindow, scroll_y);
11934
}
11935
11936
// Note that a local position will vary depending on initial scroll value,
11937
// This is a little bit confusing so bear with us:
11938
// - local_pos = (absolution_pos - window->Pos)
11939
// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
11940
// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
11941
// - They mostly exist because of legacy API.
11942
// Following the rules above, when trying to work with scrolling code, consider that:
11943
// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
11944
// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
11945
// 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
11946
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
11947
{
11948
IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
11949
window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->ScrollExpected.x); // Convert local position to scroll offset
11950
window->ScrollTargetCenterRatio.x = center_x_ratio;
11951
window->ScrollTargetEdgeSnapDist.x = 0.0f;
11952
}
11953
11954
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
11955
{
11956
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
11957
window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->ScrollExpected.y); // Convert local position to scroll offset
11958
window->ScrollTargetCenterRatio.y = center_y_ratio;
11959
window->ScrollTargetEdgeSnapDist.y = 0.0f;
11960
}
11961
11962
void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
11963
{
11964
ImGuiContext& g = *GImGui;
11965
SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
11966
}
11967
11968
void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
11969
{
11970
ImGuiContext& g = *GImGui;
11971
SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
11972
}
11973
11974
// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
11975
void ImGui::SetScrollHereX(float center_x_ratio)
11976
{
11977
ImGuiContext& g = *GImGui;
11978
ImGuiWindow* window = g.CurrentWindow;
11979
float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
11980
float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
11981
SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
11982
11983
// Tweak: snap on edges when aiming at an item very close to the edge
11984
window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
11985
}
11986
11987
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
11988
void ImGui::SetScrollHereY(float center_y_ratio)
11989
{
11990
ImGuiContext& g = *GImGui;
11991
ImGuiWindow* window = g.CurrentWindow;
11992
float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
11993
float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
11994
SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
11995
11996
// Tweak: snap on edges when aiming at an item very close to the edge
11997
window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
11998
}
11999
12000
//-----------------------------------------------------------------------------
12001
// [SECTION] TOOLTIPS
12002
//-----------------------------------------------------------------------------
12003
12004
bool ImGui::BeginTooltip()
12005
{
12006
return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
12007
}
12008
12009
bool ImGui::BeginItemTooltip()
12010
{
12011
if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
12012
return false;
12013
return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
12014
}
12015
12016
bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
12017
{
12018
ImGuiContext& g = *GImGui;
12019
12020
const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
12021
if (is_dragdrop_tooltip)
12022
{
12023
// Drag and Drop tooltips are positioning differently than other tooltips:
12024
// - offset visibility to increase visibility around mouse.
12025
// - never clamp within outer viewport boundary.
12026
// We call SetNextWindowPos() to enforce position and disable clamping.
12027
// See FindBestWindowPosForPopup() for positioning logic of other tooltips (not drag and drop ones).
12028
//ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
12029
const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
12030
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)
12031
{
12032
ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale);
12033
ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
12034
SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);
12035
}
12036
12037
SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
12038
//PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkerboard has issue with transparent colors :(
12039
tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
12040
}
12041
12042
// Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
12043
if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active && !IsWindowInBeginStack(g.TooltipPreviousWindow))
12044
{
12045
//IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
12046
SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
12047
g.TooltipOverrideCount++;
12048
}
12049
12050
const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
12051
char window_name[32];
12052
ImFormatString(window_name, IM_COUNTOF(window_name), window_name_template, g.TooltipOverrideCount);
12053
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
12054
Begin(window_name, NULL, flags | extra_window_flags);
12055
// 2023-03-09: Added bool return value to the API, but currently always returning true.
12056
// If this ever returns false we need to update BeginDragDropSource() accordingly.
12057
//if (!ret)
12058
// End();
12059
//return ret;
12060
return true;
12061
}
12062
12063
void ImGui::EndTooltip()
12064
{
12065
IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
12066
End();
12067
}
12068
12069
void ImGui::SetTooltip(const char* fmt, ...)
12070
{
12071
va_list args;
12072
va_start(args, fmt);
12073
SetTooltipV(fmt, args);
12074
va_end(args);
12075
}
12076
12077
void ImGui::SetTooltipV(const char* fmt, va_list args)
12078
{
12079
if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
12080
return;
12081
TextV(fmt, args);
12082
EndTooltip();
12083
}
12084
12085
// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
12086
// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
12087
void ImGui::SetItemTooltip(const char* fmt, ...)
12088
{
12089
va_list args;
12090
va_start(args, fmt);
12091
if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
12092
SetTooltipV(fmt, args);
12093
va_end(args);
12094
}
12095
12096
void ImGui::SetItemTooltipV(const char* fmt, va_list args)
12097
{
12098
if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
12099
SetTooltipV(fmt, args);
12100
}
12101
12102
12103
//-----------------------------------------------------------------------------
12104
// [SECTION] POPUPS
12105
//-----------------------------------------------------------------------------
12106
12107
// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
12108
bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
12109
{
12110
ImGuiContext& g = *GImGui;
12111
if (popup_flags & ImGuiPopupFlags_AnyPopupId)
12112
{
12113
// Return true if any popup is open at the current BeginPopup() level of the popup stack
12114
// This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
12115
IM_ASSERT(id == 0);
12116
if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
12117
return g.OpenPopupStack.Size > 0;
12118
else
12119
return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
12120
}
12121
else
12122
{
12123
if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
12124
{
12125
// Return true if the popup is open anywhere in the popup stack
12126
for (int n = 0; n < g.OpenPopupStack.Size; n++)
12127
if (g.OpenPopupStack[n].PopupId == id)
12128
return true;
12129
return false;
12130
}
12131
else
12132
{
12133
// Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
12134
return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
12135
}
12136
}
12137
}
12138
12139
bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
12140
{
12141
ImGuiContext& g = *GImGui;
12142
ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
12143
if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
12144
IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
12145
return IsPopupOpen(id, popup_flags);
12146
}
12147
12148
// Also see FindBlockingModal(NULL)
12149
ImGuiWindow* ImGui::GetTopMostPopupModal()
12150
{
12151
ImGuiContext& g = *GImGui;
12152
for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12153
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12154
if (popup->Flags & ImGuiWindowFlags_Modal)
12155
return popup;
12156
return NULL;
12157
}
12158
12159
// See Demo->Stacked Modal to confirm what this is for.
12160
ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
12161
{
12162
ImGuiContext& g = *GImGui;
12163
for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12164
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12165
if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
12166
return popup;
12167
return NULL;
12168
}
12169
12170
12171
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
12172
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
12173
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
12174
// - WindowA // FindBlockingModal() returns Modal1
12175
// - WindowB // .. returns Modal1
12176
// - Modal1 // .. returns Modal2
12177
// - WindowC // .. returns Modal2
12178
// - WindowD // .. returns Modal2
12179
// - Modal2 // .. returns Modal2
12180
// - WindowE // .. returns NULL
12181
// Notes:
12182
// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
12183
// Only difference is here we check for ->Active/WasActive but it may be unnecessary.
12184
ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
12185
{
12186
ImGuiContext& g = *GImGui;
12187
if (g.OpenPopupStack.Size <= 0)
12188
return NULL;
12189
12190
// Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
12191
for (ImGuiPopupData& popup_data : g.OpenPopupStack)
12192
{
12193
ImGuiWindow* popup_window = popup_data.Window;
12194
if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
12195
continue;
12196
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.
12197
continue;
12198
if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
12199
return popup_window;
12200
if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal
12201
continue;
12202
return popup_window; // Place window right below first block modal
12203
}
12204
return NULL;
12205
}
12206
12207
void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
12208
{
12209
ImGuiContext& g = *GImGui;
12210
ImGuiID id = g.CurrentWindow->GetID(str_id);
12211
IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
12212
OpenPopupEx(id, popup_flags);
12213
}
12214
12215
void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
12216
{
12217
OpenPopupEx(id, popup_flags);
12218
}
12219
12220
// Mark popup as open (toggle toward open state).
12221
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
12222
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
12223
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
12224
void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
12225
{
12226
ImGuiContext& g = *GImGui;
12227
ImGuiWindow* parent_window = g.CurrentWindow;
12228
const int current_stack_size = g.BeginPopupStack.Size;
12229
12230
if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
12231
if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
12232
return;
12233
12234
ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
12235
popup_ref.PopupId = id;
12236
popup_ref.Window = NULL;
12237
popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
12238
popup_ref.OpenFrameCount = g.FrameCount;
12239
popup_ref.OpenParentId = parent_window->IDStack.back();
12240
popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
12241
popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
12242
12243
IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
12244
if (g.OpenPopupStack.Size < current_stack_size + 1)
12245
{
12246
g.OpenPopupStack.push_back(popup_ref);
12247
}
12248
else
12249
{
12250
// Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
12251
// However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
12252
// in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
12253
// Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
12254
bool keep_existing = false;
12255
if (g.OpenPopupStack[current_stack_size].PopupId == id)
12256
if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
12257
keep_existing = true;
12258
if (keep_existing)
12259
{
12260
// No reopen
12261
g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
12262
}
12263
else
12264
{
12265
// Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
12266
ClosePopupToLevel(current_stack_size, true);
12267
g.OpenPopupStack.push_back(popup_ref);
12268
}
12269
12270
// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
12271
// This is equivalent to what ClosePopupToLevel() does.
12272
//if (g.OpenPopupStack[current_stack_size].PopupId == id)
12273
// FocusWindow(parent_window);
12274
}
12275
}
12276
12277
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
12278
// This function closes any popups that are over 'ref_window'.
12279
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
12280
{
12281
ImGuiContext& g = *GImGui;
12282
if (g.OpenPopupStack.Size == 0)
12283
return;
12284
12285
// Don't close our own child popup windows.
12286
//IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
12287
int popup_count_to_keep = 0;
12288
if (ref_window)
12289
{
12290
// Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
12291
for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
12292
{
12293
ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
12294
if (!popup.Window)
12295
continue;
12296
IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
12297
12298
// Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
12299
// - Clicking/Focusing Window2 won't close Popup1:
12300
// Window -> Popup1 -> Window2(Ref)
12301
// - Clicking/focusing Popup1 will close Popup2 and Popup3:
12302
// Window -> Popup1(Ref) -> Popup2 -> Popup3
12303
// - Each popups may contain child windows, which is why we compare ->RootWindow!
12304
// Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
12305
// We step through every popup from bottom to top to validate their position relative to reference window.
12306
bool ref_window_is_descendent_of_popup = false;
12307
for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
12308
if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
12309
if (IsWindowWithinBeginStackOf(ref_window, popup_window))
12310
{
12311
ref_window_is_descendent_of_popup = true;
12312
break;
12313
}
12314
if (!ref_window_is_descendent_of_popup)
12315
break;
12316
}
12317
}
12318
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
12319
{
12320
IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
12321
ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
12322
}
12323
}
12324
12325
void ImGui::ClosePopupsExceptModals()
12326
{
12327
ImGuiContext& g = *GImGui;
12328
12329
int popup_count_to_keep;
12330
for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
12331
{
12332
ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
12333
if (!window || (window->Flags & ImGuiWindowFlags_Modal))
12334
break;
12335
}
12336
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
12337
ClosePopupToLevel(popup_count_to_keep, true);
12338
}
12339
12340
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
12341
{
12342
ImGuiContext& g = *GImGui;
12343
IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
12344
IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
12345
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)
12346
for (int n = remaining; n < g.OpenPopupStack.Size; n++)
12347
IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"\n", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL);
12348
12349
// Trim open popup stack
12350
ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
12351
g.OpenPopupStack.resize(remaining);
12352
12353
// 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)
12354
if (restore_focus_to_window_under_popup && prev_popup.Window)
12355
{
12356
ImGuiWindow* popup_window = prev_popup.Window;
12357
ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
12358
if (focus_window && !focus_window->WasActive)
12359
FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
12360
else
12361
FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
12362
}
12363
}
12364
12365
// Close the popup we have begin-ed into.
12366
void ImGui::CloseCurrentPopup()
12367
{
12368
ImGuiContext& g = *GImGui;
12369
int popup_idx = g.BeginPopupStack.Size - 1;
12370
if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
12371
return;
12372
12373
// Closing a menu closes its top-most parent popup (unless a modal)
12374
while (popup_idx > 0)
12375
{
12376
ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
12377
ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
12378
bool close_parent = false;
12379
if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
12380
if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
12381
close_parent = true;
12382
if (!close_parent)
12383
break;
12384
popup_idx--;
12385
}
12386
IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
12387
ClosePopupToLevel(popup_idx, true);
12388
12389
// A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
12390
// To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
12391
// Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
12392
if (ImGuiWindow* window = g.NavWindow)
12393
window->DC.NavHideHighlightOneFrame = true;
12394
}
12395
12396
// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
12397
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
12398
{
12399
ImGuiContext& g = *GImGui;
12400
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12401
{
12402
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12403
return false;
12404
}
12405
12406
char name[20];
12407
IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx()
12408
ImFormatString(name, IM_COUNTOF(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame
12409
12410
bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
12411
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12412
EndPopup();
12413
//g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12414
return is_open;
12415
}
12416
12417
bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags)
12418
{
12419
ImGuiContext& g = *GImGui;
12420
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12421
{
12422
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12423
return false;
12424
}
12425
12426
char name[128];
12427
IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);
12428
ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth
12429
bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
12430
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12431
EndPopup();
12432
//g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12433
return is_open;
12434
}
12435
12436
bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
12437
{
12438
ImGuiContext& g = *GImGui;
12439
if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
12440
{
12441
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12442
return false;
12443
}
12444
flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
12445
ImGuiID id = g.CurrentWindow->GetID(str_id);
12446
return BeginPopupEx(id, flags);
12447
}
12448
12449
// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
12450
// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
12451
// - *p_open set back to false in BeginPopupModal() when popup is not open.
12452
// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
12453
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
12454
{
12455
ImGuiContext& g = *GImGui;
12456
ImGuiWindow* window = g.CurrentWindow;
12457
const ImGuiID id = window->GetID(name);
12458
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12459
{
12460
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12461
if (p_open && *p_open)
12462
*p_open = false;
12463
return false;
12464
}
12465
12466
// Center modal windows by default for increased visibility
12467
// (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
12468
// FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
12469
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)
12470
{
12471
const ImGuiViewport* viewport = GetMainViewport();
12472
SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
12473
}
12474
12475
flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
12476
const bool is_open = Begin(name, p_open, flags);
12477
if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
12478
{
12479
EndPopup();
12480
if (is_open)
12481
ClosePopupToLevel(g.BeginPopupStack.Size, true);
12482
return false;
12483
}
12484
return is_open;
12485
}
12486
12487
void ImGui::EndPopup()
12488
{
12489
ImGuiContext& g = *GImGui;
12490
ImGuiWindow* window = g.CurrentWindow;
12491
if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || g.BeginPopupStack.Size == 0)
12492
{
12493
IM_ASSERT_USER_ERROR(0, "Calling EndPopup() too many times or in wrong window!");
12494
return;
12495
}
12496
12497
// 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)
12498
if (g.NavWindow == window)
12499
NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
12500
12501
// Child-popups don't need to be laid out
12502
const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
12503
if (window->Flags & ImGuiWindowFlags_ChildWindow)
12504
g.WithinEndChildID = window->ID;
12505
End();
12506
g.WithinEndChildID = backup_within_end_child_id;
12507
}
12508
12509
// Helper to open a popup if mouse button is released over the item
12510
// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
12511
void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
12512
{
12513
ImGuiContext& g = *GImGui;
12514
ImGuiWindow* window = g.CurrentWindow;
12515
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12516
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12517
{
12518
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!
12519
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12520
OpenPopupEx(id, popup_flags);
12521
}
12522
}
12523
12524
// This is a helper to handle the simplest case of associating one named popup to one given widget.
12525
// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
12526
// - To create a popup with a specific identifier, pass it in str_id.
12527
// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
12528
// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
12529
// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
12530
// This is essentially the same as:
12531
// id = str_id ? GetID(str_id) : GetItemID();
12532
// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
12533
// return BeginPopup(id);
12534
// Which is essentially the same as:
12535
// id = str_id ? GetID(str_id) : GetItemID();
12536
// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
12537
// OpenPopup(id);
12538
// return BeginPopup(id);
12539
// The main difference being that this is tweaked to avoid computing the ID twice.
12540
bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
12541
{
12542
ImGuiContext& g = *GImGui;
12543
ImGuiWindow* window = g.CurrentWindow;
12544
if (window->SkipItems)
12545
return false;
12546
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!
12547
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12548
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12549
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12550
OpenPopupEx(id, popup_flags);
12551
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12552
}
12553
12554
bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
12555
{
12556
ImGuiContext& g = *GImGui;
12557
ImGuiWindow* window = g.CurrentWindow;
12558
if (!str_id)
12559
str_id = "window_context";
12560
ImGuiID id = window->GetID(str_id);
12561
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12562
if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12563
if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
12564
OpenPopupEx(id, popup_flags);
12565
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12566
}
12567
12568
bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
12569
{
12570
ImGuiContext& g = *GImGui;
12571
ImGuiWindow* window = g.CurrentWindow;
12572
if (!str_id)
12573
str_id = "void_context";
12574
ImGuiID id = window->GetID(str_id);
12575
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12576
if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
12577
if (GetTopMostPopupModal() == NULL)
12578
OpenPopupEx(id, popup_flags);
12579
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12580
}
12581
12582
// 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.)
12583
// 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.
12584
// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
12585
// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
12586
// this allows us to have tooltips/popups displayed out of the parent viewport.)
12587
ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
12588
{
12589
ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
12590
//GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
12591
//GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
12592
12593
// Combo Box policy (we want a connecting edge)
12594
if (policy == ImGuiPopupPositionPolicy_ComboBox)
12595
{
12596
const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
12597
for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12598
{
12599
const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n];
12600
if (n != -1 && dir == *last_dir) // Already tried this direction?
12601
continue;
12602
ImVec2 pos;
12603
if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
12604
if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
12605
if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
12606
if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
12607
if (!r_outer.Contains(ImRect(pos, pos + size)))
12608
continue;
12609
*last_dir = dir;
12610
return pos;
12611
}
12612
}
12613
12614
// Tooltip and Default popup policy
12615
// (Always first try the direction we used on the last frame, if any)
12616
if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
12617
{
12618
const ImGuiDir dir_preferred_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
12619
for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12620
{
12621
const ImGuiDir dir = (n == -1) ? *last_dir : dir_preferred_order[n];
12622
if (n != -1 && dir == *last_dir) // Already tried this direction?
12623
continue;
12624
12625
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);
12626
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);
12627
12628
// 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)
12629
if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
12630
continue;
12631
if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
12632
continue;
12633
12634
ImVec2 pos;
12635
pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
12636
pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
12637
12638
// Clamp top-left corner of popup
12639
pos.x = ImMax(pos.x, r_outer.Min.x);
12640
pos.y = ImMax(pos.y, r_outer.Min.y);
12641
12642
*last_dir = dir;
12643
return pos;
12644
}
12645
}
12646
12647
// Fallback when not enough room:
12648
*last_dir = ImGuiDir_None;
12649
12650
// For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
12651
if (policy == ImGuiPopupPositionPolicy_Tooltip)
12652
return ref_pos + ImVec2(2, 2);
12653
12654
// Otherwise try to keep within display
12655
ImVec2 pos = ref_pos;
12656
pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
12657
pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
12658
return pos;
12659
}
12660
12661
// Note that this is used for popups, which can overlap the non work-area of individual viewports.
12662
ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
12663
{
12664
ImGuiContext& g = *GImGui;
12665
IM_UNUSED(window);
12666
ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
12667
ImVec2 padding = g.Style.DisplaySafeAreaPadding;
12668
r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
12669
return r_screen;
12670
}
12671
12672
ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
12673
{
12674
ImGuiContext& g = *GImGui;
12675
12676
ImRect r_outer = GetPopupAllowedExtentRect(window);
12677
if (window->Flags & ImGuiWindowFlags_ChildMenu)
12678
{
12679
// Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
12680
// This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
12681
IM_ASSERT(g.CurrentWindow == window);
12682
ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
12683
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).
12684
ImRect r_avoid;
12685
if (parent_window->DC.MenuBarAppending)
12686
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
12687
else
12688
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);
12689
return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
12690
}
12691
if (window->Flags & ImGuiWindowFlags_Popup)
12692
{
12693
return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
12694
}
12695
if (window->Flags & ImGuiWindowFlags_Tooltip)
12696
{
12697
// Position tooltip (always follows mouse + clamp within outer boundaries)
12698
// FIXME:
12699
// - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).
12700
// - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
12701
// - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
12702
// as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
12703
IM_ASSERT(g.CurrentWindow == window);
12704
const float scale = g.Style.MouseCursorScale;
12705
const ImVec2 ref_pos = NavCalcPreferredRefPos();
12706
12707
if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
12708
{
12709
ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
12710
if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
12711
return tooltip_pos;
12712
}
12713
12714
ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
12715
ImRect r_avoid;
12716
if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
12717
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
12718
else
12719
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.
12720
//GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
12721
12722
return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
12723
}
12724
IM_ASSERT(0);
12725
return window->Pos;
12726
}
12727
12728
//-----------------------------------------------------------------------------
12729
// [SECTION] WINDOW FOCUS
12730
//----------------------------------------------------------------------------
12731
// - SetWindowFocus()
12732
// - SetNextWindowFocus()
12733
// - IsWindowFocused()
12734
// - UpdateWindowInFocusOrderList() [Internal]
12735
// - BringWindowToFocusFront() [Internal]
12736
// - BringWindowToDisplayFront() [Internal]
12737
// - BringWindowToDisplayBack() [Internal]
12738
// - BringWindowToDisplayBehind() [Internal]
12739
// - FindWindowDisplayIndex() [Internal]
12740
// - FocusWindow() [Internal]
12741
// - FocusTopMostWindowUnderOne() [Internal]
12742
//-----------------------------------------------------------------------------
12743
12744
void ImGui::SetWindowFocus()
12745
{
12746
FocusWindow(GImGui->CurrentWindow);
12747
}
12748
12749
void ImGui::SetWindowFocus(const char* name)
12750
{
12751
if (name)
12752
{
12753
if (ImGuiWindow* window = FindWindowByName(name))
12754
FocusWindow(window);
12755
}
12756
else
12757
{
12758
FocusWindow(NULL);
12759
}
12760
}
12761
12762
void ImGui::SetNextWindowFocus()
12763
{
12764
ImGuiContext& g = *GImGui;
12765
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasFocus;
12766
}
12767
12768
// Similar to IsWindowHovered()
12769
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
12770
{
12771
ImGuiContext& g = *GImGui;
12772
ImGuiWindow* ref_window = g.NavWindow;
12773
ImGuiWindow* cur_window = g.CurrentWindow;
12774
12775
if (ref_window == NULL)
12776
return false;
12777
if (flags & ImGuiFocusedFlags_AnyWindow)
12778
return true;
12779
12780
IM_ASSERT(cur_window); // Not inside a Begin()/End()
12781
const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
12782
if (flags & ImGuiFocusedFlags_RootWindow)
12783
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
12784
12785
if (flags & ImGuiFocusedFlags_ChildWindows)
12786
return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
12787
else
12788
return ref_window == cur_window;
12789
}
12790
12791
static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
12792
{
12793
ImGuiContext& g = *GImGui;
12794
IM_UNUSED(g);
12795
int order = window->FocusOrder;
12796
IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
12797
IM_ASSERT(g.WindowsFocusOrder[order] == window);
12798
return order;
12799
}
12800
12801
static void ImGui::UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
12802
{
12803
ImGuiContext& g = *GImGui;
12804
12805
const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
12806
const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
12807
if ((just_created || child_flag_changed) && !new_is_explicit_child)
12808
{
12809
IM_ASSERT(!g.WindowsFocusOrder.contains(window));
12810
g.WindowsFocusOrder.push_back(window);
12811
window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
12812
}
12813
else if (!just_created && child_flag_changed && new_is_explicit_child)
12814
{
12815
IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
12816
for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
12817
g.WindowsFocusOrder[n]->FocusOrder--;
12818
g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
12819
window->FocusOrder = -1;
12820
}
12821
window->IsExplicitChild = new_is_explicit_child;
12822
}
12823
12824
void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
12825
{
12826
ImGuiContext& g = *GImGui;
12827
IM_ASSERT(window == window->RootWindow);
12828
12829
const int cur_order = window->FocusOrder;
12830
IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
12831
if (g.WindowsFocusOrder.back() == window)
12832
return;
12833
12834
const int new_order = g.WindowsFocusOrder.Size - 1;
12835
for (int n = cur_order; n < new_order; n++)
12836
{
12837
g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
12838
g.WindowsFocusOrder[n]->FocusOrder--;
12839
IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
12840
}
12841
g.WindowsFocusOrder[new_order] = window;
12842
window->FocusOrder = (short)new_order;
12843
}
12844
12845
// Note technically focus related but rather adjacent and close to BringWindowToFocusFront()
12846
void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
12847
{
12848
ImGuiContext& g = *GImGui;
12849
ImGuiWindow* current_front_window = g.Windows.back();
12850
if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
12851
return;
12852
for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
12853
if (g.Windows[i] == window)
12854
{
12855
memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
12856
g.Windows[g.Windows.Size - 1] = window;
12857
break;
12858
}
12859
}
12860
12861
void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
12862
{
12863
ImGuiContext& g = *GImGui;
12864
if (g.Windows[0] == window)
12865
return;
12866
for (int i = 0; i < g.Windows.Size; i++)
12867
if (g.Windows[i] == window)
12868
{
12869
memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
12870
g.Windows[0] = window;
12871
break;
12872
}
12873
}
12874
12875
void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
12876
{
12877
IM_ASSERT(window != NULL && behind_window != NULL);
12878
ImGuiContext& g = *GImGui;
12879
window = window->RootWindow;
12880
behind_window = behind_window->RootWindow;
12881
int pos_wnd = FindWindowDisplayIndex(window);
12882
int pos_beh = FindWindowDisplayIndex(behind_window);
12883
if (pos_wnd < pos_beh)
12884
{
12885
size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
12886
memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
12887
g.Windows[pos_beh - 1] = window;
12888
}
12889
else
12890
{
12891
size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
12892
memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
12893
g.Windows[pos_beh] = window;
12894
}
12895
}
12896
12897
int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
12898
{
12899
ImGuiContext& g = *GImGui;
12900
return g.Windows.index_from_ptr(g.Windows.find(window));
12901
}
12902
12903
// Moving window to front of display and set focus (which happens to be back of our sorted list)
12904
void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
12905
{
12906
ImGuiContext& g = *GImGui;
12907
12908
// Modal check?
12909
if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
12910
if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
12911
{
12912
// This block would typically be reached in two situations:
12913
// - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
12914
// - User clicking on void or anything behind a modal while a modal is open (window == NULL)
12915
IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
12916
if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
12917
BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
12918
ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
12919
return;
12920
}
12921
12922
// Find last focused child (if any) and focus it instead.
12923
if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
12924
window = NavRestoreLastChildNavWindow(window);
12925
12926
// Apply focus
12927
if (g.NavWindow != window)
12928
{
12929
SetNavWindow(window);
12930
if (window && g.NavHighlightItemUnderNav)
12931
g.NavMousePosDirty = true;
12932
g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
12933
g.NavLayer = ImGuiNavLayer_Main;
12934
SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
12935
g.NavIdIsAlive = false;
12936
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12937
12938
// Close popups if any
12939
ClosePopupsOverWindow(window, false);
12940
}
12941
12942
// Move the root window to the top of the pile
12943
IM_ASSERT(window == NULL || window->RootWindow != NULL);
12944
ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
12945
ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
12946
12947
// Steal active widgets. Some of the cases it triggers includes:
12948
// - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
12949
// - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
12950
if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
12951
if (!g.ActiveIdNoClearOnFocusLoss)
12952
ClearActiveID();
12953
12954
// Passing NULL allow to disable keyboard focus
12955
if (!window)
12956
return;
12957
12958
// Bring to front
12959
BringWindowToFocusFront(focus_front_window);
12960
if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
12961
BringWindowToDisplayFront(display_front_window);
12962
}
12963
12964
void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
12965
{
12966
ImGuiContext& g = *GImGui;
12967
IM_UNUSED(filter_viewport); // Unused in master branch.
12968
int start_idx = g.WindowsFocusOrder.Size - 1;
12969
if (under_this_window != NULL)
12970
{
12971
// Aim at root window behind us, if we are in a child window that's our own root (see #4640)
12972
int offset = -1;
12973
while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
12974
{
12975
under_this_window = under_this_window->ParentWindow;
12976
offset = 0;
12977
}
12978
start_idx = FindWindowFocusIndex(under_this_window) + offset;
12979
}
12980
for (int i = start_idx; i >= 0; i--)
12981
{
12982
// 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.
12983
ImGuiWindow* window = g.WindowsFocusOrder[i];
12984
if (window == ignore_window || !window->WasActive)
12985
continue;
12986
if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
12987
{
12988
FocusWindow(window, flags);
12989
return;
12990
}
12991
}
12992
FocusWindow(NULL, flags);
12993
}
12994
12995
//-----------------------------------------------------------------------------
12996
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
12997
//-----------------------------------------------------------------------------
12998
12999
// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
13000
// In our terminology those should be interchangeable, yet right now this is super confusing.
13001
// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
13002
13003
void ImGui::SetNavCursorVisible(bool visible)
13004
{
13005
ImGuiContext& g = *GImGui;
13006
if (g.IO.ConfigNavCursorVisibleAlways)
13007
visible = true;
13008
g.NavCursorVisible = visible;
13009
}
13010
13011
// (was called NavRestoreHighlightAfterMove() before 1.91.4)
13012
void ImGui::SetNavCursorVisibleAfterMove()
13013
{
13014
ImGuiContext& g = *GImGui;
13015
if (g.IO.ConfigNavCursorVisibleAuto)
13016
g.NavCursorVisible = true;
13017
g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
13018
}
13019
13020
void ImGui::SetNavWindow(ImGuiWindow* window)
13021
{
13022
ImGuiContext& g = *GImGui;
13023
if (g.NavWindow != window)
13024
{
13025
IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
13026
g.NavWindow = window;
13027
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13028
}
13029
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13030
NavUpdateAnyRequestFlag();
13031
}
13032
13033
void ImGui::NavHighlightActivated(ImGuiID id)
13034
{
13035
ImGuiContext& g = *GImGui;
13036
g.NavHighlightActivatedId = id;
13037
g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
13038
}
13039
13040
void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
13041
{
13042
ImGuiContext& g = *GImGui;
13043
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
13044
}
13045
13046
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
13047
{
13048
ImGuiContext& g = *GImGui;
13049
IM_ASSERT(g.NavWindow != NULL);
13050
IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
13051
g.NavId = id;
13052
g.NavLayer = nav_layer;
13053
SetNavFocusScope(focus_scope_id);
13054
g.NavWindow->NavLastIds[nav_layer] = id;
13055
g.NavWindow->NavRectRel[nav_layer] = rect_rel;
13056
13057
// Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
13058
NavClearPreferredPosForAxis(ImGuiAxis_X);
13059
NavClearPreferredPosForAxis(ImGuiAxis_Y);
13060
}
13061
13062
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
13063
{
13064
ImGuiContext& g = *GImGui;
13065
IM_ASSERT(id != 0);
13066
13067
if (g.NavWindow != window)
13068
SetNavWindow(window);
13069
13070
// Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
13071
// Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
13072
const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
13073
g.NavId = id;
13074
g.NavLayer = nav_layer;
13075
SetNavFocusScope(g.CurrentFocusScopeId);
13076
window->NavLastIds[nav_layer] = id;
13077
if (g.LastItemData.ID == id)
13078
window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
13079
13080
if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
13081
g.NavHighlightItemUnderNav = true;
13082
else if (g.IO.ConfigNavCursorVisibleAuto)
13083
g.NavCursorVisible = false;
13084
13085
// Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
13086
NavClearPreferredPosForAxis(ImGuiAxis_X);
13087
NavClearPreferredPosForAxis(ImGuiAxis_Y);
13088
}
13089
13090
static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
13091
{
13092
if (ImFabs(dx) > ImFabs(dy))
13093
return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
13094
return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
13095
}
13096
13097
static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
13098
{
13099
if (cand_max < curr_min)
13100
return cand_max - curr_min;
13101
if (curr_max < cand_min)
13102
return cand_min - curr_max;
13103
return 0.0f;
13104
}
13105
13106
// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
13107
static bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb)
13108
{
13109
ImGuiContext& g = *GImGui;
13110
ImGuiWindow* window = g.CurrentWindow;
13111
if (g.NavLayer != window->DC.NavLayerCurrent)
13112
return false;
13113
13114
// FIXME: Those are not good variables names
13115
ImRect cand = nav_bb; // Current item nav rectangle
13116
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)
13117
g.NavScoringDebugCount++;
13118
13119
// When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
13120
if (window->ParentWindow == g.NavWindow)
13121
{
13122
IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
13123
if (!window->ClipRect.Overlaps(cand))
13124
return false;
13125
cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
13126
}
13127
13128
// Compute distance between boxes
13129
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
13130
float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
13131
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
13132
if (dby != 0.0f && dbx != 0.0f)
13133
dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
13134
float dist_box = ImFabs(dbx) + ImFabs(dby);
13135
13136
// 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)
13137
float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
13138
float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
13139
float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
13140
13141
// Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
13142
ImGuiDir quadrant;
13143
float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
13144
if (dbx != 0.0f || dby != 0.0f)
13145
{
13146
// For non-overlapping boxes, use distance between boxes
13147
// FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y
13148
// One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
13149
// Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
13150
dax = dbx;
13151
day = dby;
13152
dist_axial = dist_box;
13153
quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
13154
}
13155
else if (dcx != 0.0f || dcy != 0.0f)
13156
{
13157
// For overlapping boxes with different centers, use distance between centers
13158
dax = dcx;
13159
day = dcy;
13160
dist_axial = dist_center;
13161
quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
13162
}
13163
else
13164
{
13165
// 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)
13166
quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
13167
}
13168
13169
const ImGuiDir move_dir = g.NavMoveDir;
13170
#if IMGUI_DEBUG_NAV_SCORING
13171
char buf[200];
13172
if (g.IO.KeyCtrl) // Hold Ctrl to preview score in matching quadrant. Ctrl+Arrow to rotate.
13173
{
13174
if (quadrant == move_dir)
13175
{
13176
ImFormatString(buf, IM_COUNTOF(buf), "%.0f/%.0f", dist_box, dist_center);
13177
ImDrawList* draw_list = GetForegroundDrawList(window);
13178
draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
13179
draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
13180
draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
13181
}
13182
}
13183
const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
13184
const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
13185
if (debug_hovering || debug_tty)
13186
{
13187
ImFormatString(buf, IM_COUNTOF(buf),
13188
"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",
13189
dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
13190
if (debug_hovering)
13191
{
13192
ImDrawList* draw_list = GetForegroundDrawList(window);
13193
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
13194
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
13195
draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
13196
draw_list->AddText(cand.Max, ~0U, buf);
13197
}
13198
if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
13199
}
13200
#endif
13201
13202
// Is it in the quadrant we're interested in moving to?
13203
bool new_best = false;
13204
if (quadrant == move_dir)
13205
{
13206
// Does it beat the current best candidate?
13207
if (dist_box < result->DistBox)
13208
{
13209
result->DistBox = dist_box;
13210
result->DistCenter = dist_center;
13211
return true;
13212
}
13213
if (dist_box == result->DistBox)
13214
{
13215
// Try using distance between center points to break ties
13216
if (dist_center < result->DistCenter)
13217
{
13218
result->DistCenter = dist_center;
13219
new_best = true;
13220
}
13221
else if (dist_center == result->DistCenter)
13222
{
13223
// Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
13224
// (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),
13225
// 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.
13226
if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
13227
new_best = true;
13228
}
13229
}
13230
}
13231
13232
// 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
13233
// are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
13234
// 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.
13235
// 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.
13236
// Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
13237
if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
13238
if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
13239
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))
13240
{
13241
result->DistAxial = dist_axial;
13242
new_best = true;
13243
}
13244
13245
return new_best;
13246
}
13247
13248
static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
13249
{
13250
ImGuiContext& g = *GImGui;
13251
ImGuiWindow* window = g.CurrentWindow;
13252
result->Window = window;
13253
result->ID = g.LastItemData.ID;
13254
result->FocusScopeId = g.CurrentFocusScopeId;
13255
result->ItemFlags = g.LastItemData.ItemFlags;
13256
result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
13257
if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13258
{
13259
IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13260
result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13261
}
13262
}
13263
13264
// True when current work location may be scrolled horizontally when moving left / right.
13265
// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
13266
void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
13267
{
13268
ImGuiContext& g = *GImGui;
13269
ImGuiWindow* window = g.CurrentWindow;
13270
window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
13271
}
13272
13273
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
13274
// This is called after LastItemData is set, but NextItemData is also still valid.
13275
static void ImGui::NavProcessItem()
13276
{
13277
ImGuiContext& g = *GImGui;
13278
ImGuiWindow* window = g.CurrentWindow;
13279
const ImGuiID id = g.LastItemData.ID;
13280
const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
13281
13282
// When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816)
13283
ImRect nav_bb = g.LastItemData.NavRect;
13284
if (window->DC.NavIsScrollPushableX == false)
13285
{
13286
nav_bb.Min.x = ImClamp(nav_bb.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13287
nav_bb.Max.x = ImClamp(nav_bb.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13288
}
13289
13290
// Process Init Request
13291
if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
13292
{
13293
// Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
13294
const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
13295
if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
13296
{
13297
NavApplyItemToResult(&g.NavInitResult);
13298
}
13299
if (candidate_for_nav_default_focus)
13300
{
13301
g.NavInitRequest = false; // Found a match, clear request
13302
NavUpdateAnyRequestFlag();
13303
}
13304
}
13305
13306
// Process Move Request (scoring for navigation)
13307
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
13308
if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
13309
{
13310
if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
13311
{
13312
const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13313
if (is_tabbing)
13314
{
13315
NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
13316
}
13317
else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
13318
{
13319
ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
13320
if (NavScoreItem(result, nav_bb))
13321
NavApplyItemToResult(result);
13322
13323
// Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
13324
const float VISIBLE_RATIO = 0.70f;
13325
if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
13326
{
13327
const ImRect& r = window->InnerRect; // window->ClipRect
13328
if (r.Overlaps(nav_bb))
13329
if (ImClamp(nav_bb.Max.y, r.Min.y, r.Max.y) - ImClamp(nav_bb.Min.y, r.Min.y, r.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
13330
if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb))
13331
NavApplyItemToResult(&g.NavMoveResultLocalVisible);
13332
}
13333
}
13334
}
13335
}
13336
13337
// Update information for currently focused/navigated item
13338
if (g.NavId == id)
13339
{
13340
if (g.NavWindow != window)
13341
SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
13342
g.NavLayer = window->DC.NavLayerCurrent;
13343
SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
13344
g.NavFocusScopeId = g.CurrentFocusScopeId;
13345
g.NavIdIsAlive = true;
13346
if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13347
{
13348
IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13349
g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13350
}
13351
window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
13352
}
13353
}
13354
13355
// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
13356
// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
13357
// - Case 1: no nav/active id: set result to first eligible item, stop storing.
13358
// - Case 2: tab forward: on ref id set counter, on counter elapse store result
13359
// - 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
13360
// - Case 4: tab backward: store all results, on ref id pick prev, stop storing
13361
// - 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
13362
void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
13363
{
13364
ImGuiContext& g = *GImGui;
13365
13366
if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
13367
{
13368
if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
13369
return;
13370
if (g.NavFocusScopeId != g.CurrentFocusScopeId)
13371
return;
13372
}
13373
13374
// - Can always land on an item when using API call.
13375
// - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
13376
// - Tabbing without _NavEnableKeyboard: goes through inputable items only.
13377
bool can_stop;
13378
if (move_flags & ImGuiNavMoveFlags_FocusApi)
13379
can_stop = true;
13380
else
13381
can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
13382
13383
// Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
13384
ImGuiNavItemData* result = &g.NavMoveResultLocal;
13385
if (g.NavTabbingDir == +1)
13386
{
13387
// Tab Forward or SetKeyboardFocusHere() with >= 0
13388
if (can_stop && g.NavTabbingResultFirst.ID == 0)
13389
NavApplyItemToResult(&g.NavTabbingResultFirst);
13390
if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
13391
NavMoveRequestResolveWithLastItem(result);
13392
else if (g.NavId == id)
13393
g.NavTabbingCounter = 1;
13394
}
13395
else if (g.NavTabbingDir == -1)
13396
{
13397
// Tab Backward
13398
if (g.NavId == id)
13399
{
13400
if (result->ID)
13401
{
13402
g.NavMoveScoringItems = false;
13403
NavUpdateAnyRequestFlag();
13404
}
13405
}
13406
else if (can_stop)
13407
{
13408
// Keep applying until reaching NavId
13409
NavApplyItemToResult(result);
13410
}
13411
}
13412
else if (g.NavTabbingDir == 0)
13413
{
13414
if (can_stop && g.NavId == id)
13415
NavMoveRequestResolveWithLastItem(result);
13416
if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
13417
NavApplyItemToResult(&g.NavTabbingResultFirst);
13418
}
13419
}
13420
13421
bool ImGui::NavMoveRequestButNoResultYet()
13422
{
13423
ImGuiContext& g = *GImGui;
13424
return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
13425
}
13426
13427
// FIXME: ScoringRect is not set
13428
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13429
{
13430
ImGuiContext& g = *GImGui;
13431
IM_ASSERT(g.NavWindow != NULL);
13432
//IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
13433
13434
if (move_flags & ImGuiNavMoveFlags_IsTabbing)
13435
move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
13436
13437
g.NavMoveSubmitted = g.NavMoveScoringItems = true;
13438
g.NavMoveDir = move_dir;
13439
g.NavMoveDirForDebug = move_dir;
13440
g.NavMoveClipDir = clip_dir;
13441
g.NavMoveFlags = move_flags;
13442
g.NavMoveScrollFlags = scroll_flags;
13443
g.NavMoveForwardToNextFrame = false;
13444
g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
13445
g.NavMoveResultLocal.Clear();
13446
g.NavMoveResultLocalVisible.Clear();
13447
g.NavMoveResultOther.Clear();
13448
g.NavTabbingCounter = 0;
13449
g.NavTabbingResultFirst.Clear();
13450
NavUpdateAnyRequestFlag();
13451
}
13452
13453
void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
13454
{
13455
ImGuiContext& g = *GImGui;
13456
g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
13457
NavApplyItemToResult(result);
13458
NavUpdateAnyRequestFlag();
13459
}
13460
13461
// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsToParent
13462
void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data)
13463
{
13464
ImGuiContext& g = *GImGui;
13465
g.NavMoveScoringItems = false;
13466
g.LastItemData.ID = tree_node_data->ID;
13467
g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
13468
g.LastItemData.NavRect = tree_node_data->NavRect;
13469
NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
13470
NavClearPreferredPosForAxis(ImGuiAxis_Y);
13471
NavUpdateAnyRequestFlag();
13472
}
13473
13474
void ImGui::NavMoveRequestCancel()
13475
{
13476
ImGuiContext& g = *GImGui;
13477
g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13478
NavUpdateAnyRequestFlag();
13479
}
13480
13481
// Forward will reuse the move request again on the next frame (generally with modifications done to it)
13482
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13483
{
13484
ImGuiContext& g = *GImGui;
13485
IM_ASSERT(g.NavMoveForwardToNextFrame == false);
13486
NavMoveRequestCancel();
13487
g.NavMoveForwardToNextFrame = true;
13488
g.NavMoveDir = move_dir;
13489
g.NavMoveClipDir = clip_dir;
13490
g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
13491
g.NavMoveScrollFlags = scroll_flags;
13492
}
13493
13494
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
13495
// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
13496
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
13497
{
13498
ImGuiContext& g = *GImGui;
13499
IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
13500
13501
// In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
13502
// as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
13503
if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
13504
g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
13505
}
13506
13507
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
13508
// This way we could find the last focused window among our children. It would be much less confusing this way?
13509
static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
13510
{
13511
ImGuiWindow* parent = nav_window;
13512
while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
13513
parent = parent->ParentWindow;
13514
if (parent && parent != nav_window)
13515
parent->NavLastChildNavWindow = nav_window;
13516
}
13517
13518
// Restore the last focused child.
13519
// Call when we are expected to land on the Main Layer (0) after FocusWindow()
13520
static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
13521
{
13522
if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
13523
return window->NavLastChildNavWindow;
13524
return window;
13525
}
13526
13527
void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
13528
{
13529
ImGuiContext& g = *GImGui;
13530
if (layer == ImGuiNavLayer_Main)
13531
{
13532
ImGuiWindow* prev_nav_window = g.NavWindow;
13533
g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
13534
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13535
if (prev_nav_window)
13536
IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
13537
}
13538
ImGuiWindow* window = g.NavWindow;
13539
if (window->NavLastIds[layer] != 0)
13540
{
13541
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
13542
}
13543
else
13544
{
13545
g.NavLayer = layer;
13546
NavInitWindow(window, true);
13547
}
13548
}
13549
13550
static inline void ImGui::NavUpdateAnyRequestFlag()
13551
{
13552
ImGuiContext& g = *GImGui;
13553
g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
13554
if (g.NavAnyRequest)
13555
IM_ASSERT(g.NavWindow != NULL);
13556
}
13557
13558
// This needs to be called before we submit any widget (aka in or before Begin)
13559
void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
13560
{
13561
ImGuiContext& g = *GImGui;
13562
IM_ASSERT(window == g.NavWindow);
13563
13564
if (window->Flags & ImGuiWindowFlags_NoNavInputs)
13565
{
13566
g.NavId = 0;
13567
SetNavFocusScope(window->NavRootFocusScopeId);
13568
return;
13569
}
13570
13571
bool init_for_nav = false;
13572
if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
13573
init_for_nav = true;
13574
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
13575
if (init_for_nav)
13576
{
13577
SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
13578
g.NavInitRequest = true;
13579
g.NavInitRequestFromMove = false;
13580
g.NavInitResult.ID = 0;
13581
NavUpdateAnyRequestFlag();
13582
}
13583
else
13584
{
13585
g.NavId = window->NavLastIds[0];
13586
SetNavFocusScope(window->NavRootFocusScopeId);
13587
}
13588
}
13589
13590
static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
13591
{
13592
ImGuiContext& g = *GImGui;
13593
ImGuiWindow* window = g.NavWindow;
13594
const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13595
13596
// 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.
13597
if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
13598
return ImGuiInputSource_Mouse;
13599
else
13600
return ImGuiInputSource_Keyboard; // or Nav in general
13601
}
13602
13603
static ImVec2 ImGui::NavCalcPreferredRefPos()
13604
{
13605
ImGuiContext& g = *GImGui;
13606
ImGuiWindow* window = g.NavWindow;
13607
ImGuiInputSource source = NavCalcPreferredRefPosSource();
13608
13609
const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13610
13611
if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL)
13612
source = ImGuiInputSource_Mouse;
13613
13614
// 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.
13615
if (source == ImGuiInputSource_Mouse)
13616
{
13617
// Mouse (we need a fallback in case the mouse becomes invalid after being used)
13618
// 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.
13619
// In theory we could move that +1.0f offset in OpenPopupEx()
13620
ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
13621
return ImVec2(p.x + 1.0f, p.y);
13622
}
13623
else
13624
{
13625
// When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
13626
ImRect ref_rect;
13627
if (activated_shortcut)
13628
ref_rect = g.LastItemData.NavRect;
13629
else if (window != NULL)
13630
ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
13631
13632
// Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
13633
if (window != NULL && window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
13634
{
13635
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
13636
ref_rect.Translate(window->Scroll - next_scroll);
13637
}
13638
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()));
13639
ImGuiViewport* viewport = GetMainViewport();
13640
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.
13641
}
13642
}
13643
13644
float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
13645
{
13646
ImGuiContext& g = *GImGui;
13647
float repeat_delay, repeat_rate;
13648
GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
13649
13650
ImGuiKey key_less, key_more;
13651
if (g.NavInputSource == ImGuiInputSource_Gamepad)
13652
{
13653
key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
13654
key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
13655
}
13656
else
13657
{
13658
key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
13659
key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
13660
}
13661
float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
13662
if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
13663
amount = 0.0f;
13664
return amount;
13665
}
13666
13667
static void ImGui::NavUpdate()
13668
{
13669
ImGuiContext& g = *GImGui;
13670
ImGuiIO& io = g.IO;
13671
13672
io.WantSetMousePos = false;
13673
//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);
13674
13675
// Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
13676
// FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
13677
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13678
const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
13679
if (nav_gamepad_active)
13680
for (ImGuiKey key : nav_gamepad_keys_to_change_source)
13681
if (IsKeyDown(key))
13682
g.NavInputSource = ImGuiInputSource_Gamepad;
13683
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13684
const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
13685
if (nav_keyboard_active)
13686
for (ImGuiKey key : nav_keyboard_keys_to_change_source)
13687
if (IsKeyDown(key))
13688
g.NavInputSource = ImGuiInputSource_Keyboard;
13689
13690
// Process navigation init request (select first/default focus)
13691
g.NavJustMovedToId = 0;
13692
g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
13693
if (g.NavInitResult.ID != 0)
13694
NavInitRequestApplyResult();
13695
g.NavInitRequest = false;
13696
g.NavInitRequestFromMove = false;
13697
g.NavInitResult.ID = 0;
13698
13699
// Process navigation move request
13700
if (g.NavMoveSubmitted)
13701
NavMoveRequestApplyResult();
13702
g.NavTabbingCounter = 0;
13703
g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13704
if (g.NavCursorHideFrames > 0)
13705
if (--g.NavCursorHideFrames == 0)
13706
g.NavCursorVisible = true;
13707
13708
// Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
13709
bool set_mouse_pos = false;
13710
if (g.NavMousePosDirty && g.NavIdIsAlive)
13711
if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
13712
set_mouse_pos = true;
13713
g.NavMousePosDirty = false;
13714
IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
13715
13716
// 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
13717
if (g.NavWindow)
13718
NavSaveLastChildNavWindowIntoParent(g.NavWindow);
13719
if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
13720
g.NavWindow->NavLastChildNavWindow = NULL;
13721
13722
// Update Ctrl+Tab and Windowing features (hold Square to move/resize/etc.)
13723
NavUpdateWindowing();
13724
13725
// Set output flags for user application
13726
io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
13727
io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
13728
13729
// Process NavCancel input (to close a popup, get back to parent, clear focus)
13730
NavUpdateCancelRequest();
13731
13732
// Process manual activation request
13733
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
13734
g.NavActivateFlags = ImGuiActivateFlags_None;
13735
if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13736
{
13737
const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
13738
const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
13739
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)) */;
13740
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)) */);
13741
if (g.ActiveId == 0 && activate_pressed)
13742
{
13743
g.NavActivateId = g.NavId;
13744
g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
13745
}
13746
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
13747
{
13748
g.NavActivateId = g.NavId;
13749
g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
13750
}
13751
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
13752
g.NavActivateDownId = g.NavId;
13753
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
13754
{
13755
g.NavActivatePressedId = g.NavId;
13756
NavHighlightActivated(g.NavId);
13757
}
13758
}
13759
if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13760
g.NavCursorVisible = false;
13761
else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
13762
g.NavCursorVisible = true;
13763
if (g.NavActivateId != 0)
13764
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
13765
13766
// Highlight
13767
if (g.NavHighlightActivatedTimer > 0.0f)
13768
g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
13769
if (g.NavHighlightActivatedTimer == 0.0f)
13770
g.NavHighlightActivatedId = 0;
13771
13772
// Process programmatic activation request
13773
// FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
13774
if (g.NavNextActivateId != 0)
13775
{
13776
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
13777
g.NavActivateFlags = g.NavNextActivateFlags;
13778
}
13779
g.NavNextActivateId = 0;
13780
13781
// Process move requests
13782
NavUpdateCreateMoveRequest();
13783
if (g.NavMoveDir == ImGuiDir_None)
13784
NavUpdateCreateTabbingRequest();
13785
NavUpdateAnyRequestFlag();
13786
g.NavIdIsAlive = false;
13787
13788
// Scrolling
13789
if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
13790
{
13791
// *Fallback* manual-scroll with Nav directional keys when window has no navigable item
13792
ImGuiWindow* window = g.NavWindow;
13793
const float scroll_speed = IM_ROUND(window->FontRefSize * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
13794
const ImGuiDir move_dir = g.NavMoveDir;
13795
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
13796
{
13797
if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
13798
SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
13799
if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
13800
SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
13801
}
13802
13803
// *Normal* Manual scroll with LStick
13804
// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
13805
if (nav_gamepad_active)
13806
{
13807
const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
13808
const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
13809
if (scroll_dir.x != 0.0f && window->ScrollbarX)
13810
SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
13811
if (scroll_dir.y != 0.0f)
13812
SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
13813
}
13814
}
13815
13816
// Always prioritize mouse highlight if navigation is disabled
13817
if (!nav_keyboard_active && !nav_gamepad_active)
13818
{
13819
g.NavCursorVisible = false;
13820
g.NavHighlightItemUnderNav = set_mouse_pos = false;
13821
}
13822
13823
// Update mouse position if requested
13824
// (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)
13825
if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
13826
TeleportMousePos(NavCalcPreferredRefPos());
13827
13828
// [DEBUG]
13829
g.NavScoringDebugCount = 0;
13830
#if IMGUI_DEBUG_NAV_RECTS
13831
if (ImGuiWindow* debug_window = g.NavWindow)
13832
{
13833
ImDrawList* draw_list = GetForegroundDrawList(debug_window);
13834
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)); }
13835
//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); }
13836
}
13837
#endif
13838
}
13839
13840
void ImGui::NavInitRequestApplyResult()
13841
{
13842
// 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)
13843
ImGuiContext& g = *GImGui;
13844
if (!g.NavWindow)
13845
return;
13846
13847
ImGuiNavItemData* result = &g.NavInitResult;
13848
if (g.NavId != result->ID)
13849
{
13850
g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13851
g.NavJustMovedToId = result->ID;
13852
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13853
g.NavJustMovedToKeyMods = 0;
13854
g.NavJustMovedToIsTabbing = false;
13855
g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13856
}
13857
13858
// Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
13859
// FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
13860
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13861
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13862
g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
13863
if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13864
g.NavLastValidSelectionUserData = result->SelectionUserData;
13865
if (g.NavInitRequestFromMove)
13866
SetNavCursorVisibleAfterMove();
13867
}
13868
13869
// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
13870
static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
13871
{
13872
// Bias initial rect
13873
ImGuiContext& g = *GImGui;
13874
const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
13875
13876
// Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
13877
// - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
13878
// - But each successful move sets new bias on one axis, only cleared when using mouse.
13879
if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
13880
{
13881
if (preferred_pos_rel.x == FLT_MAX)
13882
preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
13883
if (preferred_pos_rel.y == FLT_MAX)
13884
preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
13885
}
13886
13887
// Apply general bias on the other axis
13888
if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
13889
r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
13890
else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
13891
r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
13892
}
13893
13894
void ImGui::NavUpdateCreateMoveRequest()
13895
{
13896
ImGuiContext& g = *GImGui;
13897
ImGuiIO& io = g.IO;
13898
ImGuiWindow* window = g.NavWindow;
13899
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13900
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13901
13902
if (g.NavMoveForwardToNextFrame && window != NULL)
13903
{
13904
// 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)
13905
// (preserve most state, which were already set by the NavMoveRequestForward() function)
13906
IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
13907
IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
13908
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
13909
}
13910
else
13911
{
13912
// Initiate directional inputs request
13913
g.NavMoveDir = ImGuiDir_None;
13914
g.NavMoveFlags = ImGuiNavMoveFlags_None;
13915
g.NavMoveScrollFlags = ImGuiScrollFlags_None;
13916
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
13917
{
13918
const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
13919
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; }
13920
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; }
13921
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; }
13922
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; }
13923
}
13924
g.NavMoveClipDir = g.NavMoveDir;
13925
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
13926
}
13927
13928
// Update PageUp/PageDown/Home/End scroll
13929
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
13930
float scoring_page_offset_y = 0.0f;
13931
if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
13932
scoring_page_offset_y = NavUpdatePageUpPageDown();
13933
13934
// [DEBUG] Always send a request when holding Ctrl. Hold Ctrl + Arrow change the direction.
13935
#if IMGUI_DEBUG_NAV_SCORING
13936
//if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
13937
// g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
13938
if (io.KeyCtrl)
13939
{
13940
if (g.NavMoveDir == ImGuiDir_None)
13941
g.NavMoveDir = g.NavMoveDirForDebug;
13942
g.NavMoveClipDir = g.NavMoveDir;
13943
g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
13944
}
13945
#endif
13946
13947
// Submit
13948
g.NavMoveForwardToNextFrame = false;
13949
if (g.NavMoveDir != ImGuiDir_None)
13950
NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
13951
13952
// Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
13953
if (g.NavMoveSubmitted && g.NavId == 0)
13954
{
13955
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
13956
g.NavInitRequest = g.NavInitRequestFromMove = true;
13957
g.NavInitResult.ID = 0;
13958
if (g.IO.ConfigNavCursorVisibleAuto)
13959
g.NavCursorVisible = true;
13960
}
13961
13962
// When using gamepad, we project the reference nav bounding box into window visible area.
13963
// This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
13964
// since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
13965
if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
13966
{
13967
bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
13968
bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
13969
ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
13970
13971
// Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
13972
// Otherwise 'inner_rect_rel' would be off on the move result frame.
13973
inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
13974
13975
if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
13976
{
13977
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
13978
float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f);
13979
float pad_y = ImMin(inner_rect_rel.GetHeight(), window->FontRefSize * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item
13980
inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
13981
inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
13982
inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
13983
inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
13984
window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
13985
g.NavId = 0;
13986
}
13987
}
13988
13989
// Prepare scoring rectangle.
13990
// 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)
13991
ImRect scoring_rect;
13992
if (window != NULL)
13993
{
13994
ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
13995
scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
13996
13997
if (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)
13998
{
13999
// When we start from a visible location, score visible items and prioritize this result.
14000
if (window->InnerRect.Contains(scoring_rect))
14001
g.NavMoveFlags |= ImGuiNavMoveFlags_AlsoScoreVisibleSet;
14002
g.NavScoringNoClipRect = scoring_rect;
14003
scoring_rect.TranslateY(scoring_page_offset_y);
14004
g.NavScoringNoClipRect.Add(scoring_rect);
14005
}
14006
14007
//GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Pre-bias
14008
if (g.NavMoveSubmitted)
14009
NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
14010
IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
14011
//GetForegroundDrawList()->AddRectFilled(scoring_rect.Min - ImVec2(1, 1), scoring_rect.Max + ImVec2(1, 1), IM_COL32(255, 100, 0, 80)); // [DEBUG] Post-bias
14012
//if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRectFilled(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(100, 255, 0, 80)); } // [DEBUG]
14013
}
14014
g.NavScoringRect = scoring_rect;
14015
//g.NavScoringNoClipRect.Add(scoring_rect);
14016
}
14017
14018
void ImGui::NavUpdateCreateTabbingRequest()
14019
{
14020
ImGuiContext& g = *GImGui;
14021
ImGuiWindow* window = g.NavWindow;
14022
IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
14023
if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
14024
return;
14025
14026
const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
14027
if (!tab_pressed)
14028
return;
14029
14030
// Initiate tabbing request
14031
// (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
14032
// See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
14033
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14034
if (nav_keyboard_active)
14035
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
14036
else
14037
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
14038
ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
14039
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
14040
ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
14041
NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
14042
g.NavTabbingCounter = -1;
14043
}
14044
14045
// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
14046
void ImGui::NavMoveRequestApplyResult()
14047
{
14048
ImGuiContext& g = *GImGui;
14049
#if IMGUI_DEBUG_NAV_SCORING
14050
if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
14051
return;
14052
#endif
14053
14054
// Select which result to use
14055
ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
14056
14057
// Tabbing forward wrap
14058
if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
14059
if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
14060
result = &g.NavTabbingResultFirst;
14061
14062
// 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)
14063
const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
14064
if (result == NULL)
14065
{
14066
if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
14067
g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
14068
if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
14069
SetNavCursorVisibleAfterMove();
14070
NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
14071
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
14072
return;
14073
}
14074
14075
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
14076
if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
14077
if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
14078
result = &g.NavMoveResultLocalVisible;
14079
14080
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
14081
if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
14082
if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
14083
result = &g.NavMoveResultOther;
14084
IM_ASSERT(g.NavWindow && result->Window);
14085
14086
// Scroll to keep newly navigated item fully into view.
14087
if (g.NavLayer == ImGuiNavLayer_Main)
14088
{
14089
ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
14090
ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
14091
14092
if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
14093
{
14094
// FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
14095
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
14096
SetScrollY(result->Window, scroll_target);
14097
}
14098
}
14099
14100
if (g.NavWindow != result->Window)
14101
{
14102
IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
14103
g.NavWindow = result->Window;
14104
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
14105
}
14106
14107
// Clear active id unless requested not to
14108
// FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
14109
// so this is mostly provided as a gateway for further experiments (see #1418, #2890)
14110
if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
14111
ClearActiveID();
14112
14113
// Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
14114
// PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
14115
if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
14116
{
14117
g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
14118
g.NavJustMovedToId = result->ID;
14119
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
14120
g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
14121
g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
14122
g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
14123
//IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
14124
}
14125
14126
// Apply new NavID/Focus
14127
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
14128
ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
14129
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
14130
if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
14131
g.NavLastValidSelectionUserData = result->SelectionUserData;
14132
14133
// Restore last preferred position for current axis
14134
// (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
14135
if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
14136
{
14137
preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
14138
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
14139
}
14140
14141
// Tabbing: Activates Inputable, otherwise only Focus
14142
if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
14143
g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
14144
14145
// Activate
14146
if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
14147
{
14148
g.NavNextActivateId = result->ID;
14149
g.NavNextActivateFlags = ImGuiActivateFlags_None;
14150
if (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)
14151
g.NavNextActivateFlags |= ImGuiActivateFlags_FromFocusApi;
14152
if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
14153
g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
14154
}
14155
14156
// Make nav cursor visible
14157
if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
14158
SetNavCursorVisibleAfterMove();
14159
}
14160
14161
// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
14162
// FIXME: In order to support e.g. Escape to clear a selection we'll need:
14163
// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
14164
// - 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
14165
static void ImGui::NavUpdateCancelRequest()
14166
{
14167
ImGuiContext& g = *GImGui;
14168
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14169
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14170
if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
14171
return;
14172
14173
IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
14174
if (g.ActiveId != 0)
14175
{
14176
ClearActiveID();
14177
}
14178
else if (g.NavLayer != ImGuiNavLayer_Main)
14179
{
14180
// Leave the "menu" layer
14181
NavRestoreLayer(ImGuiNavLayer_Main);
14182
SetNavCursorVisibleAfterMove();
14183
}
14184
else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow &&
14185
!(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) &&
14186
g.NavWindow->RootWindowForNav->ParentWindow && !(g.NavWindow->ChildFlags & ImGuiChildFlags_NoNavCancel))
14187
{
14188
// Exit child window
14189
ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
14190
ImGuiWindow* parent_window = child_window->ParentWindow;
14191
IM_ASSERT(child_window->ChildId != 0);
14192
FocusWindow(parent_window);
14193
SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
14194
SetNavCursorVisibleAfterMove();
14195
}
14196
else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
14197
{
14198
// Close open popup/menu
14199
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
14200
}
14201
#if 0
14202
// DUCKSTATION-CHANGE: We want to keep nav active, since we handle menu exits ourselves.
14203
else
14204
{
14205
// Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
14206
// FIXME-NAV: This should happen on window appearing.
14207
if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14208
if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
14209
g.NavWindow->NavLastIds[0] = 0;
14210
14211
// Clear nav focus
14212
if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14213
g.NavId = 0;
14214
if (g.IO.ConfigNavEscapeClearFocusWindow)
14215
FocusWindow(NULL);
14216
}
14217
#endif
14218
}
14219
14220
// Handle PageUp/PageDown/Home/End keys
14221
// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
14222
// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
14223
// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
14224
static float ImGui::NavUpdatePageUpPageDown()
14225
{
14226
ImGuiContext& g = *GImGui;
14227
ImGuiWindow* window = g.NavWindow;
14228
if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
14229
return 0.0f;
14230
14231
const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_GamepadL2, ImGuiKeyOwner_NoOwner);
14232
const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_GamepadR2, ImGuiKeyOwner_NoOwner);
14233
const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14234
const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14235
if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
14236
return 0.0f;
14237
14238
if (g.NavLayer != ImGuiNavLayer_Main)
14239
NavRestoreLayer(ImGuiNavLayer_Main);
14240
14241
if ((window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Main)) == 0 && window->DC.NavWindowHasScrollY)
14242
{
14243
// Fallback manual-scroll when window has no navigable item
14244
if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_GamepadL2, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14245
SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
14246
else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_GamepadR2, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14247
SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
14248
else if (home_pressed)
14249
SetScrollY(window, 0.0f);
14250
else if (end_pressed)
14251
SetScrollY(window, window->ScrollMax.y);
14252
}
14253
else
14254
{
14255
ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
14256
const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight());
14257
float nav_scoring_rect_offset_y = 0.0f;
14258
if (IsKeyPressed(ImGuiKey_PageUp, true) || IsKeyPressed(ImGuiKey_GamepadL2, true))
14259
{
14260
nav_scoring_rect_offset_y = -page_offset_y;
14261
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)
14262
g.NavMoveClipDir = ImGuiDir_Up;
14263
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later
14264
}
14265
else if (IsKeyPressed(ImGuiKey_PageDown, true) || IsKeyPressed(ImGuiKey_GamepadR2, true))
14266
{
14267
nav_scoring_rect_offset_y = +page_offset_y;
14268
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)
14269
g.NavMoveClipDir = ImGuiDir_Down;
14270
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_IsPageMove; // ImGuiNavMoveFlags_AlsoScoreVisibleSet may be added later
14271
}
14272
else if (home_pressed)
14273
{
14274
// FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
14275
// Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
14276
// Preserve current horizontal position if we have any.
14277
nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
14278
if (nav_rect_rel.IsInverted())
14279
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14280
g.NavMoveDir = ImGuiDir_Down;
14281
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14282
// FIXME-NAV: MoveClipDir left to _None, intentional?
14283
}
14284
else if (end_pressed)
14285
{
14286
nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
14287
if (nav_rect_rel.IsInverted())
14288
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14289
g.NavMoveDir = ImGuiDir_Up;
14290
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14291
// FIXME-NAV: MoveClipDir left to _None, intentional?
14292
}
14293
return nav_scoring_rect_offset_y;
14294
}
14295
return 0.0f;
14296
}
14297
14298
static void ImGui::NavEndFrame()
14299
{
14300
ImGuiContext& g = *GImGui;
14301
14302
// Show Ctrl+Tab list window
14303
if (g.NavWindowingTarget != NULL)
14304
NavUpdateWindowingOverlay();
14305
14306
// Perform wrap-around in menus
14307
// 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.
14308
// FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
14309
if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
14310
NavUpdateCreateWrappingRequest();
14311
}
14312
14313
static void ImGui::NavUpdateCreateWrappingRequest()
14314
{
14315
ImGuiContext& g = *GImGui;
14316
ImGuiWindow* window = g.NavWindow;
14317
14318
bool do_forward = false;
14319
ImRect bb_rel = window->NavRectRel[g.NavLayer];
14320
ImGuiDir clip_dir = g.NavMoveDir;
14321
14322
const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
14323
//const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
14324
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14325
{
14326
bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
14327
if (move_flags & ImGuiNavMoveFlags_WrapX)
14328
{
14329
bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
14330
clip_dir = ImGuiDir_Up;
14331
}
14332
do_forward = true;
14333
}
14334
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14335
{
14336
bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
14337
if (move_flags & ImGuiNavMoveFlags_WrapX)
14338
{
14339
bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
14340
clip_dir = ImGuiDir_Down;
14341
}
14342
do_forward = true;
14343
}
14344
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14345
{
14346
bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
14347
if (move_flags & ImGuiNavMoveFlags_WrapY)
14348
{
14349
bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
14350
clip_dir = ImGuiDir_Left;
14351
}
14352
SetScrollY(window, window->ScrollMax.y);
14353
do_forward = true;
14354
}
14355
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14356
{
14357
bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
14358
if (move_flags & ImGuiNavMoveFlags_WrapY)
14359
{
14360
bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
14361
clip_dir = ImGuiDir_Right;
14362
}
14363
SetScrollY(window, 0.0f);
14364
do_forward = true;
14365
}
14366
if (!do_forward)
14367
return;
14368
window->NavRectRel[g.NavLayer] = bb_rel;
14369
NavClearPreferredPosForAxis(ImGuiAxis_X);
14370
NavClearPreferredPosForAxis(ImGuiAxis_Y);
14371
NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
14372
}
14373
14374
// Can we focus this window with Ctrl+Tab (or PadMenu + PadFocusPrev/PadFocusNext)
14375
// Note that NoNavFocus makes the window not reachable with Ctrl+Tab but it can still be focused with mouse or programmatically.
14376
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
14377
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
14378
{
14379
return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
14380
}
14381
14382
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
14383
{
14384
ImGuiContext& g = *GImGui;
14385
for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
14386
if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
14387
return g.WindowsFocusOrder[i];
14388
return NULL;
14389
}
14390
14391
static void NavUpdateWindowingTarget(int focus_change_dir)
14392
{
14393
ImGuiContext& g = *GImGui;
14394
IM_ASSERT(g.NavWindowingTarget);
14395
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
14396
return;
14397
14398
const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
14399
ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
14400
if (!window_target)
14401
window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
14402
if (window_target) // Don't reset windowing target if there's a single window in the list
14403
{
14404
g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
14405
g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14406
}
14407
g.NavWindowingToggleLayer = false;
14408
}
14409
14410
// Apply focus and close overlay
14411
static void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window)
14412
{
14413
ImGuiContext& g = *GImGui;
14414
if (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)
14415
{
14416
ClearActiveID();
14417
SetNavCursorVisibleAfterMove();
14418
ClosePopupsOverWindow(apply_focus_window, false);
14419
FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
14420
IM_ASSERT(g.NavWindow != NULL);
14421
apply_focus_window = g.NavWindow;
14422
if (apply_focus_window->NavLastIds[0] == 0)
14423
NavInitWindow(apply_focus_window, false);
14424
14425
// If the window has ONLY a menu layer (no main layer), select it directly
14426
// Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
14427
// so Ctrl+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
14428
// the target window as already been previewed once.
14429
// FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
14430
// we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
14431
// won't be valid.
14432
if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
14433
g.NavLayer = ImGuiNavLayer_Menu;
14434
}
14435
g.NavWindowingTarget = NULL;
14436
}
14437
14438
// Windowing management mode
14439
// Keyboard: Ctrl+Tab (change focus/move/resize), Alt (toggle menu layer)
14440
// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
14441
static void ImGui::NavUpdateWindowing()
14442
{
14443
ImGuiContext& g = *GImGui;
14444
ImGuiIO& io = g.IO;
14445
14446
ImGuiWindow* apply_focus_window = NULL;
14447
bool apply_toggle_layer = false;
14448
14449
ImGuiWindow* modal_window = GetTopMostPopupModal();
14450
bool allow_windowing = false;// (modal_window == NULL); // FIXME: This prevent Ctrl+Tab from being usable with windows that are inside the Begin-stack of that modal.
14451
if (!allow_windowing)
14452
g.NavWindowingTarget = NULL;
14453
14454
// Fade out
14455
if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
14456
{
14457
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
14458
if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
14459
g.NavWindowingTargetAnim = NULL;
14460
}
14461
14462
// Start Ctrl+Tab or Square+L/R window selection
14463
// (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
14464
const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing");
14465
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14466
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14467
const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14468
const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14469
const bool start_toggling_with_gamepad = false;// nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id);
14470
const bool start_windowing_with_gamepad = allow_windowing && start_toggling_with_gamepad;
14471
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
14472
bool just_started_windowing_from_null_focus = false;
14473
if (start_toggling_with_gamepad)
14474
{
14475
g.NavWindowingToggleLayer = true; // Gamepad starts toggling layer
14476
g.NavWindowingToggleKey = ImGuiKey_NavGamepadMenu;
14477
g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad;
14478
}
14479
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
14480
if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
14481
{
14482
if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad)
14483
g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
14484
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
14485
g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14486
g.NavWindowingInputSource = g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
14487
if (g.NavWindow == NULL)
14488
just_started_windowing_from_null_focus = true;
14489
14490
// 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.
14491
if (keyboard_next_window || keyboard_prev_window)
14492
SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
14493
}
14494
14495
// Gamepad update
14496
if ((g.NavWindowingTarget || g.NavWindowingToggleLayer) && g.NavWindowingInputSource == ImGuiInputSource_Gamepad)
14497
{
14498
if (g.NavWindowingTarget != NULL)
14499
{
14500
// Highlight only appears after a brief time holding the button, so that a fast tap on ImGuiKey_NavGamepadMenu (to toggle NavLayer) doesn't add visual noise
14501
// However inputs are accepted immediately, so you press ImGuiKey_NavGamepadMenu + L1/R1 fast.
14502
g.NavWindowingTimer += io.DeltaTime;
14503
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
14504
14505
// Select window to focus
14506
const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
14507
if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
14508
{
14509
NavUpdateWindowingTarget(focus_change_dir);
14510
g.NavWindowingHighlightAlpha = 1.0f;
14511
}
14512
}
14513
14514
// Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
14515
if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
14516
{
14517
g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
14518
if (g.NavWindowingToggleLayer && g.NavWindow)
14519
apply_toggle_layer = true;
14520
else if (!g.NavWindowingToggleLayer)
14521
apply_focus_window = g.NavWindowingTarget;
14522
g.NavWindowingTarget = NULL;
14523
g.NavWindowingToggleLayer = false;
14524
}
14525
}
14526
14527
// Keyboard: Focus
14528
if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)
14529
{
14530
// Visuals only appears after a brief time after pressing TAB the first time, so that a fast Ctrl+Tab doesn't add visual noise
14531
ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
14532
IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
14533
g.NavWindowingTimer += io.DeltaTime;
14534
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
14535
if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
14536
NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);
14537
else if ((io.KeyMods & shared_mods) != shared_mods)
14538
apply_focus_window = g.NavWindowingTarget;
14539
}
14540
14541
// Keyboard: Press and Release Alt to toggle menu layer
14542
const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
14543
bool windowing_toggle_layer_start = false;
14544
if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
14545
for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
14546
if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
14547
{
14548
windowing_toggle_layer_start = true;
14549
g.NavWindowingToggleLayer = true;
14550
g.NavWindowingToggleKey = windowing_toggle_key;
14551
g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Keyboard;
14552
break;
14553
}
14554
if (g.NavWindowingToggleLayer && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)
14555
{
14556
// We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
14557
// We cancel toggling nav layer when other modifiers are pressed. (See #4439)
14558
// - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
14559
// We cancel toggling nav layer if an owner has claimed the key.
14560
if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
14561
g.NavWindowingToggleLayer = false;
14562
else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)
14563
g.NavWindowingToggleLayer = false;
14564
else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
14565
g.NavWindowingToggleLayer = false;
14566
14567
// Apply layer toggle on Alt release
14568
// 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.
14569
if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
14570
if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
14571
if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
14572
apply_toggle_layer = true;
14573
if (!IsKeyDown(g.NavWindowingToggleKey))
14574
g.NavWindowingToggleLayer = false;
14575
}
14576
14577
// Move window
14578
if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
14579
{
14580
ImVec2 nav_move_dir;
14581
if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
14582
nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
14583
if (g.NavInputSource == ImGuiInputSource_Gamepad)
14584
nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
14585
if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
14586
{
14587
const float NAV_MOVE_SPEED = 800.0f;
14588
const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
14589
g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
14590
g.NavHighlightItemUnderNav = true;
14591
ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
14592
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
14593
{
14594
ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
14595
SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
14596
g.NavWindowingAccumDeltaPos -= accum_floored;
14597
}
14598
}
14599
}
14600
14601
// Apply final focus
14602
if (apply_focus_window)
14603
NavUpdateWindowingApplyFocus(apply_focus_window);
14604
14605
// Apply menu/layer toggle
14606
if (apply_toggle_layer && g.NavWindow)
14607
{
14608
ClearActiveID();
14609
14610
// Move to parent menu if necessary
14611
ImGuiWindow* new_nav_window = g.NavWindow;
14612
while (new_nav_window->ParentWindow
14613
&& (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
14614
&& (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
14615
&& (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
14616
new_nav_window = new_nav_window->ParentWindow;
14617
if (new_nav_window != g.NavWindow)
14618
{
14619
ImGuiWindow* old_nav_window = g.NavWindow;
14620
FocusWindow(new_nav_window);
14621
new_nav_window->NavLastChildNavWindow = old_nav_window;
14622
}
14623
14624
// Toggle layer
14625
const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
14626
if (new_nav_layer != g.NavLayer)
14627
{
14628
// Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
14629
if (new_nav_layer == ImGuiNavLayer_Menu)
14630
g.NavWindow->NavLastIds[new_nav_layer] = 0;
14631
NavRestoreLayer(new_nav_layer);
14632
SetNavCursorVisibleAfterMove();
14633
}
14634
}
14635
}
14636
14637
// Window has already passed the IsWindowNavFocusable()
14638
static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
14639
{
14640
if (window->Flags & ImGuiWindowFlags_Popup)
14641
return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
14642
if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
14643
return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
14644
return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
14645
}
14646
14647
// Overlay displayed when using Ctrl+Tab. Called by EndFrame().
14648
void ImGui::NavUpdateWindowingOverlay()
14649
{
14650
ImGuiContext& g = *GImGui;
14651
IM_ASSERT(g.NavWindowingTarget != NULL);
14652
14653
if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
14654
return;
14655
14656
const ImGuiViewport* viewport = GetMainViewport();
14657
SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
14658
SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
14659
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
14660
Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
14661
g.NavWindowingListWindow = g.CurrentWindow;
14662
if (g.ContextName[0] != 0)
14663
SeparatorText(g.ContextName);
14664
for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
14665
{
14666
ImGuiWindow* window = g.WindowsFocusOrder[n];
14667
IM_ASSERT(window != NULL); // Fix static analyzers
14668
if (!IsWindowNavFocusable(window))
14669
continue;
14670
const char* label = window->Name;
14671
if (label == FindRenderedTextEnd(label))
14672
label = GetFallbackWindowNameForWindowingList(window);
14673
Selectable(label, g.NavWindowingTarget == window);
14674
}
14675
End();
14676
PopStyleVar();
14677
}
14678
14679
//-----------------------------------------------------------------------------
14680
// [SECTION] DRAG AND DROP
14681
//-----------------------------------------------------------------------------
14682
14683
bool ImGui::IsDragDropActive()
14684
{
14685
ImGuiContext& g = *GImGui;
14686
return g.DragDropActive;
14687
}
14688
14689
void ImGui::ClearDragDrop()
14690
{
14691
ImGuiContext& g = *GImGui;
14692
if (g.DragDropActive)
14693
IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
14694
g.DragDropActive = false;
14695
g.DragDropPayload.Clear();
14696
g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags_None;
14697
g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
14698
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
14699
g.DragDropAcceptFrameCount = -1;
14700
14701
g.DragDropPayloadBufHeap.clear();
14702
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14703
}
14704
14705
bool ImGui::BeginTooltipHidden()
14706
{
14707
ImGuiContext& g = *GImGui;
14708
bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
14709
SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
14710
return ret;
14711
}
14712
14713
// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
14714
// If the item has an identifier:
14715
// - This assume/require the item to be activated (typically via ButtonBehavior).
14716
// - 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.
14717
// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
14718
// If the item has no identifier:
14719
// - Currently always assume left mouse button.
14720
bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
14721
{
14722
ImGuiContext& g = *GImGui;
14723
ImGuiWindow* window = g.CurrentWindow;
14724
14725
// FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
14726
// in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
14727
ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
14728
14729
bool source_drag_active = false;
14730
ImGuiID source_id = 0;
14731
ImGuiID source_parent_id = 0;
14732
if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
14733
{
14734
source_id = g.LastItemData.ID;
14735
if (source_id != 0)
14736
{
14737
// Common path: items with ID
14738
if (g.ActiveId != source_id)
14739
return false;
14740
if (g.ActiveIdMouseButton != -1)
14741
mouse_button = g.ActiveIdMouseButton;
14742
if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14743
return false;
14744
g.ActiveIdAllowOverlap = false;
14745
}
14746
else
14747
{
14748
// Uncommon path: items without ID
14749
if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14750
return false;
14751
if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
14752
return false;
14753
14754
// If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
14755
// A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
14756
if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
14757
{
14758
IM_ASSERT(0);
14759
return false;
14760
}
14761
14762
// Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
14763
// We build a throwaway ID based on current ID stack + relative AABB of items in window.
14764
// THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
14765
// We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
14766
// Rely on keeping other window->LastItemXXX fields intact.
14767
source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
14768
KeepAliveID(source_id);
14769
bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);
14770
if (is_hovered && g.IO.MouseClicked[mouse_button])
14771
{
14772
SetActiveID(source_id, window);
14773
FocusWindow(window);
14774
}
14775
if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
14776
g.ActiveIdAllowOverlap = is_hovered;
14777
}
14778
if (g.ActiveId != source_id)
14779
return false;
14780
source_parent_id = window->IDStack.back();
14781
source_drag_active = IsMouseDragging(mouse_button);
14782
14783
// Disable navigation and key inputs while dragging + cancel existing request if any
14784
SetActiveIdUsingAllKeyboardKeys();
14785
}
14786
else
14787
{
14788
// When ImGuiDragDropFlags_SourceExtern is set:
14789
window = NULL;
14790
source_id = ImHashStr("#SourceExtern");
14791
source_drag_active = true;
14792
mouse_button = g.IO.MouseDown[0] ? 0 : -1;
14793
KeepAliveID(source_id);
14794
SetActiveID(source_id, NULL);
14795
}
14796
14797
IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14798
if (!source_drag_active)
14799
return false;
14800
14801
// Activate drag and drop
14802
if (!g.DragDropActive)
14803
{
14804
IM_ASSERT(source_id != 0);
14805
ClearDragDrop();
14806
IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
14807
source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
14808
ImGuiPayload& payload = g.DragDropPayload;
14809
payload.SourceId = source_id;
14810
payload.SourceParentId = source_parent_id;
14811
g.DragDropActive = true;
14812
g.DragDropSourceFlags = flags;
14813
g.DragDropMouseButton = mouse_button;
14814
if (payload.SourceId == g.ActiveId)
14815
g.ActiveIdNoClearOnFocusLoss = true;
14816
}
14817
g.DragDropSourceFrameCount = g.FrameCount;
14818
g.DragDropWithinSource = true;
14819
14820
if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14821
{
14822
// Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
14823
// We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
14824
bool ret;
14825
if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlagsPrev & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
14826
ret = BeginTooltipHidden();
14827
else
14828
ret = BeginTooltip();
14829
IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddenAndSkipItemsForCurrentFrame().
14830
IM_UNUSED(ret);
14831
}
14832
14833
if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
14834
g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
14835
14836
return true;
14837
}
14838
14839
void ImGui::EndDragDropSource()
14840
{
14841
ImGuiContext& g = *GImGui;
14842
IM_ASSERT(g.DragDropActive);
14843
IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
14844
14845
if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14846
EndTooltip();
14847
14848
// Discard the drag if have not called SetDragDropPayload()
14849
if (g.DragDropPayload.DataFrameCount == -1)
14850
ClearDragDrop();
14851
g.DragDropWithinSource = false;
14852
}
14853
14854
// Use 'cond' to choose to submit payload on drag start or every frame
14855
bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
14856
{
14857
ImGuiContext& g = *GImGui;
14858
ImGuiPayload& payload = g.DragDropPayload;
14859
if (cond == 0)
14860
cond = ImGuiCond_Always;
14861
14862
IM_ASSERT(type != NULL);
14863
IM_ASSERT(ImStrlen(type) < IM_COUNTOF(payload.DataType) && "Payload type can be at most 32 characters long");
14864
IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
14865
IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
14866
IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
14867
14868
if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
14869
{
14870
// Copy payload
14871
ImStrncpy(payload.DataType, type, IM_COUNTOF(payload.DataType));
14872
g.DragDropPayloadBufHeap.resize(0);
14873
if (data_size > sizeof(g.DragDropPayloadBufLocal))
14874
{
14875
// Store in heap
14876
g.DragDropPayloadBufHeap.resize((int)data_size);
14877
payload.Data = g.DragDropPayloadBufHeap.Data;
14878
memcpy(payload.Data, data, data_size);
14879
}
14880
else if (data_size > 0)
14881
{
14882
// Store locally
14883
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14884
payload.Data = g.DragDropPayloadBufLocal;
14885
memcpy(payload.Data, data, data_size);
14886
}
14887
else
14888
{
14889
payload.Data = NULL;
14890
}
14891
payload.DataSize = (int)data_size;
14892
}
14893
payload.DataFrameCount = g.FrameCount;
14894
14895
// Return whether the payload has been accepted
14896
return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
14897
}
14898
14899
bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
14900
{
14901
ImGuiContext& g = *GImGui;
14902
if (!g.DragDropActive)
14903
return false;
14904
14905
ImGuiWindow* window = g.CurrentWindow;
14906
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14907
if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
14908
return false;
14909
IM_ASSERT(id != 0);
14910
if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
14911
return false;
14912
if (window->SkipItems)
14913
return false;
14914
14915
IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14916
g.DragDropTargetRect = bb;
14917
g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
14918
g.DragDropTargetId = id;
14919
g.DragDropTargetFullViewport = 0;
14920
g.DragDropWithinTarget = true;
14921
return true;
14922
}
14923
14924
// Typical usage would be:
14925
// if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
14926
// if (ImGui::BeginDragDropTargetViewport(ImGui::GetMainViewport(), NULL))
14927
// But we are leaving the hover test to the caller for maximum flexibility.
14928
bool ImGui::BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb)
14929
{
14930
ImGuiContext& g = *GImGui;
14931
if (!g.DragDropActive)
14932
return false;
14933
14934
ImRect bb = p_bb ? *p_bb : ((ImGuiViewportP*)viewport)->GetWorkRect();
14935
ImGuiID id = viewport->ID;
14936
if (!IsMouseHoveringRect(bb.Min, bb.Max, false) || (id == g.DragDropPayload.SourceId))
14937
return false;
14938
14939
IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14940
g.DragDropTargetRect = bb;
14941
g.DragDropTargetClipRect = bb;
14942
g.DragDropTargetId = id;
14943
g.DragDropTargetFullViewport = id;
14944
g.DragDropWithinTarget = true;
14945
return true;
14946
}
14947
14948
// We don't use BeginDragDropTargetCustom() and duplicate its code because:
14949
// 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.
14950
// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
14951
// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
14952
bool ImGui::BeginDragDropTarget()
14953
{
14954
ImGuiContext& g = *GImGui;
14955
if (!g.DragDropActive)
14956
return false;
14957
14958
ImGuiWindow* window = g.CurrentWindow;
14959
if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
14960
return false;
14961
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14962
if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)
14963
return false;
14964
14965
const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
14966
ImGuiID id = g.LastItemData.ID;
14967
if (id == 0)
14968
{
14969
id = window->GetIDFromRectangle(display_rect);
14970
KeepAliveID(id);
14971
}
14972
if (g.DragDropPayload.SourceId == id)
14973
return false;
14974
14975
IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14976
g.DragDropTargetRect = display_rect;
14977
g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
14978
g.DragDropTargetId = id;
14979
g.DragDropWithinTarget = true;
14980
return true;
14981
}
14982
14983
bool ImGui::IsDragDropPayloadBeingAccepted()
14984
{
14985
ImGuiContext& g = *GImGui;
14986
return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
14987
}
14988
14989
const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
14990
{
14991
ImGuiContext& g = *GImGui;
14992
ImGuiPayload& payload = g.DragDropPayload;
14993
IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
14994
IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
14995
if (type != NULL && !payload.IsDataType(type))
14996
return NULL;
14997
14998
// Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
14999
// NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
15000
const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
15001
ImRect r = g.DragDropTargetRect;
15002
float r_surface = r.GetWidth() * r.GetHeight();
15003
if (r_surface > g.DragDropAcceptIdCurrRectSurface)
15004
return NULL;
15005
15006
g.DragDropAcceptFlagsCurr = flags;
15007
g.DragDropAcceptIdCurr = g.DragDropTargetId;
15008
g.DragDropAcceptIdCurrRectSurface = r_surface;
15009
//IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
15010
15011
// Render default drop visuals
15012
payload.Preview = was_accepted_previously;
15013
flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
15014
const bool draw_target_rect = payload.Preview && !(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect);
15015
if (draw_target_rect && g.DragDropTargetFullViewport != 0)
15016
{
15017
ImRect bb = g.DragDropTargetRect;
15018
bb.Expand(-3.5f);
15019
RenderDragDropTargetRectEx(GetForegroundDrawList(), bb);
15020
}
15021
else if (draw_target_rect)
15022
{
15023
RenderDragDropTargetRectForItem(r);
15024
}
15025
15026
g.DragDropAcceptFrameCount = g.FrameCount;
15027
if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
15028
payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
15029
else
15030
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()
15031
if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
15032
return NULL;
15033
15034
if (payload.Delivery)
15035
IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
15036
return &payload;
15037
}
15038
15039
// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
15040
void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb)
15041
{
15042
ImGuiContext& g = *GImGui;
15043
ImGuiWindow* window = g.CurrentWindow;
15044
ImRect bb_display = bb;
15045
bb_display.ClipWith(g.DragDropTargetClipRect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
15046
bb_display.Expand(g.Style.DragDropTargetPadding);
15047
bool push_clip_rect = !window->ClipRect.Contains(bb_display);
15048
if (push_clip_rect)
15049
window->DrawList->PushClipRectFullScreen();
15050
RenderDragDropTargetRectEx(window->DrawList, bb_display);
15051
if (push_clip_rect)
15052
window->DrawList->PopClipRect();
15053
}
15054
15055
void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb)
15056
{
15057
ImGuiContext& g = *GImGui;
15058
draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), g.Style.DragDropTargetRounding, 0);
15059
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), g.Style.DragDropTargetRounding, 0, g.Style.DragDropTargetBorderSize);
15060
}
15061
15062
const ImGuiPayload* ImGui::GetDragDropPayload()
15063
{
15064
ImGuiContext& g = *GImGui;
15065
return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
15066
}
15067
15068
void ImGui::EndDragDropTarget()
15069
{
15070
ImGuiContext& g = *GImGui;
15071
IM_ASSERT(g.DragDropActive);
15072
IM_ASSERT(g.DragDropWithinTarget);
15073
g.DragDropWithinTarget = false;
15074
15075
// Clear drag and drop state payload right after delivery
15076
if (g.DragDropPayload.Delivery)
15077
ClearDragDrop();
15078
}
15079
15080
//-----------------------------------------------------------------------------
15081
// [SECTION] LOGGING/CAPTURING
15082
//-----------------------------------------------------------------------------
15083
// All text output from the interface can be captured into tty/file/clipboard.
15084
// By default, tree nodes are automatically opened during logging.
15085
//-----------------------------------------------------------------------------
15086
15087
// Pass text data straight to log (without being displayed)
15088
static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
15089
{
15090
if (g.LogFile)
15091
{
15092
g.LogBuffer.Buf.resize(0);
15093
g.LogBuffer.appendfv(fmt, args);
15094
ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
15095
}
15096
else
15097
{
15098
g.LogBuffer.appendfv(fmt, args);
15099
}
15100
}
15101
15102
void ImGui::LogText(const char* fmt, ...)
15103
{
15104
ImGuiContext& g = *GImGui;
15105
if (!g.LogEnabled)
15106
return;
15107
15108
va_list args;
15109
va_start(args, fmt);
15110
LogTextV(g, fmt, args);
15111
va_end(args);
15112
}
15113
15114
void ImGui::LogTextV(const char* fmt, va_list args)
15115
{
15116
ImGuiContext& g = *GImGui;
15117
if (!g.LogEnabled)
15118
return;
15119
15120
LogTextV(g, fmt, args);
15121
}
15122
15123
// Internal version that takes a position to decide on newline placement and pad items according to their depth.
15124
// We split text into individual lines to add current tree level padding
15125
// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
15126
void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
15127
{
15128
ImGuiContext& g = *GImGui;
15129
ImGuiWindow* window = g.CurrentWindow;
15130
15131
const char* prefix = g.LogNextPrefix;
15132
const char* suffix = g.LogNextSuffix;
15133
g.LogNextPrefix = g.LogNextSuffix = NULL;
15134
15135
if (!text_end)
15136
text_end = FindRenderedTextEnd(text, text_end);
15137
15138
const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
15139
if (ref_pos)
15140
g.LogLinePosY = ref_pos->y;
15141
if (log_new_line)
15142
{
15143
LogText(IM_NEWLINE);
15144
g.LogLineFirstItem = true;
15145
}
15146
15147
if (prefix)
15148
LogRenderedText(ref_pos, prefix, prefix + ImStrlen(prefix)); // Calculate end ourself to ensure "##" are included here.
15149
15150
// Re-adjust padding if we have popped out of our starting depth
15151
if (g.LogDepthRef > window->DC.TreeDepth)
15152
g.LogDepthRef = window->DC.TreeDepth;
15153
const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
15154
15155
const char* text_remaining = text;
15156
for (;;)
15157
{
15158
// Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
15159
// We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
15160
const char* line_start = text_remaining;
15161
const char* line_end = ImStreolRange(line_start, text_end);
15162
const bool is_last_line = (line_end == text_end);
15163
if (line_start != line_end || !is_last_line)
15164
{
15165
const int line_length = (int)(line_end - line_start);
15166
const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
15167
LogText("%*s%.*s", indentation, "", line_length, line_start);
15168
g.LogLineFirstItem = false;
15169
if (*line_end == '\n')
15170
{
15171
LogText(IM_NEWLINE);
15172
g.LogLineFirstItem = true;
15173
}
15174
}
15175
if (is_last_line)
15176
break;
15177
text_remaining = line_end + 1;
15178
}
15179
15180
if (suffix)
15181
LogRenderedText(ref_pos, suffix, suffix + ImStrlen(suffix));
15182
}
15183
15184
// Start logging/capturing text output
15185
void ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth)
15186
{
15187
ImGuiContext& g = *GImGui;
15188
ImGuiWindow* window = g.CurrentWindow;
15189
IM_ASSERT(g.LogEnabled == false);
15190
IM_ASSERT(g.LogFile == NULL && g.LogBuffer.empty());
15191
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiLogFlags_OutputMask_)); // Check that only 1 type flag is used
15192
15193
g.LogEnabled = g.ItemUnclipByLog = true;
15194
g.LogFlags = flags;
15195
g.LogWindow = window;
15196
g.LogNextPrefix = g.LogNextSuffix = NULL;
15197
g.LogDepthRef = window->DC.TreeDepth;
15198
g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
15199
g.LogLinePosY = FLT_MAX;
15200
g.LogLineFirstItem = true;
15201
}
15202
15203
// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
15204
void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
15205
{
15206
ImGuiContext& g = *GImGui;
15207
g.LogNextPrefix = prefix;
15208
g.LogNextSuffix = suffix;
15209
}
15210
15211
void ImGui::LogToTTY(int auto_open_depth)
15212
{
15213
ImGuiContext& g = *GImGui;
15214
if (g.LogEnabled)
15215
return;
15216
IM_UNUSED(auto_open_depth);
15217
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15218
LogBegin(ImGuiLogFlags_OutputTTY, auto_open_depth);
15219
g.LogFile = stdout;
15220
#endif
15221
}
15222
15223
// Start logging/capturing text output to given file
15224
void ImGui::LogToFile(int auto_open_depth, const char* filename)
15225
{
15226
ImGuiContext& g = *GImGui;
15227
if (g.LogEnabled)
15228
return;
15229
15230
// FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
15231
// be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
15232
// By opening the file in binary mode "ab" we have consistent output everywhere.
15233
if (!filename)
15234
filename = g.IO.LogFilename;
15235
if (!filename || !filename[0])
15236
return;
15237
ImFileHandle f = ImFileOpen(filename, "ab");
15238
if (!f)
15239
{
15240
IM_ASSERT(0);
15241
return;
15242
}
15243
15244
LogBegin(ImGuiLogFlags_OutputFile, auto_open_depth);
15245
g.LogFile = f;
15246
}
15247
15248
// Start logging/capturing text output to clipboard
15249
void ImGui::LogToClipboard(int auto_open_depth)
15250
{
15251
ImGuiContext& g = *GImGui;
15252
if (g.LogEnabled)
15253
return;
15254
LogBegin(ImGuiLogFlags_OutputClipboard, auto_open_depth);
15255
}
15256
15257
void ImGui::LogToBuffer(int auto_open_depth)
15258
{
15259
ImGuiContext& g = *GImGui;
15260
if (g.LogEnabled)
15261
return;
15262
LogBegin(ImGuiLogFlags_OutputBuffer, auto_open_depth);
15263
}
15264
15265
void ImGui::LogFinish()
15266
{
15267
ImGuiContext& g = *GImGui;
15268
if (!g.LogEnabled)
15269
return;
15270
15271
LogText(IM_NEWLINE);
15272
switch (g.LogFlags & ImGuiLogFlags_OutputMask_)
15273
{
15274
case ImGuiLogFlags_OutputTTY:
15275
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15276
fflush(g.LogFile);
15277
#endif
15278
break;
15279
case ImGuiLogFlags_OutputFile:
15280
ImFileClose(g.LogFile);
15281
break;
15282
case ImGuiLogFlags_OutputBuffer:
15283
break;
15284
case ImGuiLogFlags_OutputClipboard:
15285
if (!g.LogBuffer.empty())
15286
SetClipboardText(g.LogBuffer.begin());
15287
break;
15288
default:
15289
IM_ASSERT(0);
15290
break;
15291
}
15292
15293
g.LogEnabled = g.ItemUnclipByLog = false;
15294
g.LogFlags = ImGuiLogFlags_None;
15295
g.LogFile = NULL;
15296
g.LogBuffer.clear();
15297
}
15298
15299
// Helper to display logging buttons
15300
// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
15301
void ImGui::LogButtons()
15302
{
15303
ImGuiContext& g = *GImGui;
15304
15305
PushID("LogButtons");
15306
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15307
const bool log_to_tty = Button("Log To TTY"); SameLine();
15308
#else
15309
const bool log_to_tty = false;
15310
#endif
15311
const bool log_to_file = Button("Log To File"); SameLine();
15312
const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
15313
PushItemFlag(ImGuiItemFlags_NoTabStop, true);
15314
SetNextItemWidth(CalcTextSize("999").x);
15315
SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
15316
PopItemFlag();
15317
PopID();
15318
15319
// Start logging at the end of the function so that the buttons don't appear in the log
15320
if (log_to_tty)
15321
LogToTTY();
15322
if (log_to_file)
15323
LogToFile();
15324
if (log_to_clipboard)
15325
LogToClipboard();
15326
}
15327
15328
//-----------------------------------------------------------------------------
15329
// [SECTION] SETTINGS
15330
//-----------------------------------------------------------------------------
15331
// - UpdateSettings() [Internal]
15332
// - MarkIniSettingsDirty() [Internal]
15333
// - FindSettingsHandler() [Internal]
15334
// - ClearIniSettings() [Internal]
15335
// - LoadIniSettingsFromDisk()
15336
// - LoadIniSettingsFromMemory()
15337
// - SaveIniSettingsToDisk()
15338
// - SaveIniSettingsToMemory()
15339
//-----------------------------------------------------------------------------
15340
// - CreateNewWindowSettings() [Internal]
15341
// - FindWindowSettingsByID() [Internal]
15342
// - FindWindowSettingsByWindow() [Internal]
15343
// - ClearWindowSettings() [Internal]
15344
// - WindowSettingsHandler_***() [Internal]
15345
//-----------------------------------------------------------------------------
15346
15347
// Called by NewFrame()
15348
void ImGui::UpdateSettings()
15349
{
15350
// Load settings on first frame (if not explicitly loaded manually before)
15351
ImGuiContext& g = *GImGui;
15352
if (!g.SettingsLoaded)
15353
{
15354
IM_ASSERT(g.SettingsWindows.empty());
15355
if (g.IO.IniFilename)
15356
LoadIniSettingsFromDisk(g.IO.IniFilename);
15357
g.SettingsLoaded = true;
15358
}
15359
15360
// Save settings (with a delay after the last modification, so we don't spam disk too much)
15361
if (g.SettingsDirtyTimer > 0.0f)
15362
{
15363
g.SettingsDirtyTimer -= g.IO.DeltaTime;
15364
if (g.SettingsDirtyTimer <= 0.0f)
15365
{
15366
if (g.IO.IniFilename != NULL)
15367
SaveIniSettingsToDisk(g.IO.IniFilename);
15368
else
15369
g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
15370
g.SettingsDirtyTimer = 0.0f;
15371
}
15372
}
15373
}
15374
15375
void ImGui::MarkIniSettingsDirty()
15376
{
15377
ImGuiContext& g = *GImGui;
15378
if (g.SettingsDirtyTimer <= 0.0f)
15379
g.SettingsDirtyTimer = g.IO.IniSavingRate;
15380
}
15381
15382
void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
15383
{
15384
ImGuiContext& g = *GImGui;
15385
if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
15386
if (g.SettingsDirtyTimer <= 0.0f)
15387
g.SettingsDirtyTimer = g.IO.IniSavingRate;
15388
}
15389
15390
void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
15391
{
15392
ImGuiContext& g = *GImGui;
15393
IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
15394
g.SettingsHandlers.push_back(*handler);
15395
}
15396
15397
void ImGui::RemoveSettingsHandler(const char* type_name)
15398
{
15399
ImGuiContext& g = *GImGui;
15400
if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
15401
g.SettingsHandlers.erase(handler);
15402
}
15403
15404
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
15405
{
15406
ImGuiContext& g = *GImGui;
15407
const ImGuiID type_hash = ImHashStr(type_name);
15408
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15409
if (handler.TypeHash == type_hash)
15410
return &handler;
15411
return NULL;
15412
}
15413
15414
// Clear all settings (windows, tables, docking etc.)
15415
void ImGui::ClearIniSettings()
15416
{
15417
ImGuiContext& g = *GImGui;
15418
g.SettingsIniData.clear();
15419
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15420
if (handler.ClearAllFn != NULL)
15421
handler.ClearAllFn(&g, &handler);
15422
}
15423
15424
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
15425
{
15426
size_t file_data_size = 0;
15427
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
15428
if (!file_data)
15429
return;
15430
if (file_data_size > 0)
15431
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
15432
IM_FREE(file_data);
15433
}
15434
15435
// Zero-tolerance, no error reporting, cheap .ini parsing
15436
// 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!
15437
void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
15438
{
15439
ImGuiContext& g = *GImGui;
15440
IM_ASSERT(g.Initialized);
15441
//IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
15442
//IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
15443
15444
// For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
15445
// 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..
15446
if (ini_size == 0)
15447
ini_size = ImStrlen(ini_data);
15448
g.SettingsIniData.Buf.resize((int)ini_size + 1);
15449
char* const buf = g.SettingsIniData.Buf.Data;
15450
char* const buf_end = buf + ini_size;
15451
memcpy(buf, ini_data, ini_size);
15452
buf_end[0] = 0;
15453
15454
// Call pre-read handlers
15455
// Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
15456
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15457
if (handler.ReadInitFn != NULL)
15458
handler.ReadInitFn(&g, &handler);
15459
15460
void* entry_data = NULL;
15461
ImGuiSettingsHandler* entry_handler = NULL;
15462
15463
char* line_end = NULL;
15464
for (char* line = buf; line < buf_end; line = line_end + 1)
15465
{
15466
// Skip new lines markers, then find end of the line
15467
while (*line == '\n' || *line == '\r')
15468
line++;
15469
line_end = line;
15470
while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
15471
line_end++;
15472
line_end[0] = 0;
15473
if (line[0] == ';')
15474
continue;
15475
if (line[0] == '[' && line_end > line && line_end[-1] == ']')
15476
{
15477
// Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
15478
line_end[-1] = 0;
15479
const char* name_end = line_end - 1;
15480
const char* type_start = line + 1;
15481
char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
15482
const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
15483
if (!type_end || !name_start)
15484
continue;
15485
*type_end = 0; // Overwrite first ']'
15486
name_start++; // Skip second '['
15487
entry_handler = FindSettingsHandler(type_start);
15488
entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
15489
}
15490
else if (entry_handler != NULL && entry_data != NULL)
15491
{
15492
// Let type handler parse the line
15493
entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
15494
}
15495
}
15496
g.SettingsLoaded = true;
15497
15498
// [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
15499
memcpy(buf, ini_data, ini_size);
15500
15501
// Call post-read handlers
15502
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15503
if (handler.ApplyAllFn != NULL)
15504
handler.ApplyAllFn(&g, &handler);
15505
}
15506
15507
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
15508
{
15509
ImGuiContext& g = *GImGui;
15510
g.SettingsDirtyTimer = 0.0f;
15511
if (!ini_filename)
15512
return;
15513
15514
size_t ini_data_size = 0;
15515
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
15516
ImFileHandle f = ImFileOpen(ini_filename, "wt");
15517
if (!f)
15518
return;
15519
ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
15520
ImFileClose(f);
15521
}
15522
15523
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
15524
const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
15525
{
15526
ImGuiContext& g = *GImGui;
15527
g.SettingsDirtyTimer = 0.0f;
15528
g.SettingsIniData.Buf.resize(0);
15529
g.SettingsIniData.Buf.push_back(0);
15530
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15531
handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
15532
if (out_size)
15533
*out_size = (size_t)g.SettingsIniData.size();
15534
return g.SettingsIniData.c_str();
15535
}
15536
15537
ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
15538
{
15539
ImGuiContext& g = *GImGui;
15540
15541
// Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
15542
if (g.IO.ConfigDebugIniSettings == false)
15543
name = ImHashSkipUncontributingPrefix(name);
15544
const size_t name_len = ImStrlen(name);
15545
15546
// Allocate chunk
15547
const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
15548
ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
15549
IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
15550
settings->ID = ImHashStr(name, name_len);
15551
memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
15552
15553
return settings;
15554
}
15555
15556
// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
15557
// This is called once per window .ini entry + once per newly instantiated window.
15558
ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
15559
{
15560
ImGuiContext& g = *GImGui;
15561
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15562
if (settings->ID == id && !settings->WantDelete)
15563
return settings;
15564
return NULL;
15565
}
15566
15567
// This is faster if you are holding on a Window already as we don't need to perform a search.
15568
ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
15569
{
15570
ImGuiContext& g = *GImGui;
15571
if (window->SettingsOffset != -1)
15572
return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
15573
return FindWindowSettingsByID(window->ID);
15574
}
15575
15576
// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
15577
void ImGui::ClearWindowSettings(const char* name)
15578
{
15579
//IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
15580
ImGuiWindow* window = FindWindowByName(name);
15581
if (window != NULL)
15582
{
15583
window->Flags |= ImGuiWindowFlags_NoSavedSettings;
15584
InitOrLoadWindowSettings(window, NULL);
15585
}
15586
if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
15587
settings->WantDelete = true;
15588
}
15589
15590
static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15591
{
15592
ImGuiContext& g = *ctx;
15593
for (ImGuiWindow* window : g.Windows)
15594
window->SettingsOffset = -1;
15595
g.SettingsWindows.clear();
15596
}
15597
15598
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15599
{
15600
ImGuiID id = ImHashStr(name);
15601
ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
15602
if (settings)
15603
*settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
15604
else
15605
settings = ImGui::CreateNewWindowSettings(name);
15606
settings->ID = id;
15607
settings->WantApply = true;
15608
return (void*)settings;
15609
}
15610
15611
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
15612
{
15613
ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
15614
int x, y;
15615
int i;
15616
if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
15617
else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
15618
else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
15619
else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); }
15620
}
15621
15622
// Apply to existing windows (if any)
15623
static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15624
{
15625
ImGuiContext& g = *ctx;
15626
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15627
if (settings->WantApply)
15628
{
15629
if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
15630
ApplyWindowSettings(window, settings);
15631
settings->WantApply = false;
15632
}
15633
}
15634
15635
static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15636
{
15637
// Gather data from windows that were active during this session
15638
// (if a window wasn't opened in this session we preserve its settings)
15639
ImGuiContext& g = *ctx;
15640
for (ImGuiWindow* window : g.Windows)
15641
{
15642
if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
15643
continue;
15644
15645
ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
15646
if (!settings)
15647
{
15648
settings = ImGui::CreateNewWindowSettings(window->Name);
15649
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
15650
}
15651
IM_ASSERT(settings->ID == window->ID);
15652
settings->Pos = ImVec2ih(window->Pos);
15653
settings->Size = ImVec2ih(window->SizeFull);
15654
settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;
15655
settings->Collapsed = window->Collapsed;
15656
settings->WantDelete = false;
15657
}
15658
15659
// Write to text buffer
15660
buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
15661
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15662
{
15663
if (settings->WantDelete)
15664
continue;
15665
const char* settings_name = settings->GetName();
15666
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
15667
if (settings->IsChild)
15668
{
15669
buf->appendf("IsChild=1\n");
15670
buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15671
}
15672
else
15673
{
15674
buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
15675
buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15676
if (settings->Collapsed)
15677
buf->appendf("Collapsed=1\n");
15678
}
15679
buf->append("\n");
15680
}
15681
}
15682
15683
//-----------------------------------------------------------------------------
15684
// [SECTION] LOCALIZATION
15685
//-----------------------------------------------------------------------------
15686
15687
void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
15688
{
15689
ImGuiContext& g = *GImGui;
15690
for (int n = 0; n < count; n++)
15691
g.LocalizationTable[entries[n].Key] = entries[n].Text;
15692
}
15693
15694
//-----------------------------------------------------------------------------
15695
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
15696
//-----------------------------------------------------------------------------
15697
// - GetMainViewport()
15698
// - SetWindowViewport() [Internal]
15699
// - ScaleWindowsInViewport() [Internal]
15700
// - UpdateViewportsNewFrame() [Internal]
15701
// (this section is more complete in the 'docking' branch)
15702
//-----------------------------------------------------------------------------
15703
15704
void ImGuiPlatformIO::ClearPlatformHandlers()
15705
{
15706
Platform_GetClipboardTextFn = NULL;
15707
Platform_SetClipboardTextFn = NULL;
15708
Platform_ClipboardUserData = NULL;
15709
Platform_OpenInShellFn = NULL;
15710
Platform_OpenInShellUserData = NULL;
15711
Platform_SetImeDataFn = NULL;
15712
Platform_ImeUserData = NULL;
15713
}
15714
15715
void ImGuiPlatformIO::ClearRendererHandlers()
15716
{
15717
Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0;
15718
Renderer_RenderState = NULL;
15719
}
15720
15721
ImGuiViewport* ImGui::GetMainViewport()
15722
{
15723
ImGuiContext& g = *GImGui;
15724
return g.Viewports[0];
15725
}
15726
15727
void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
15728
{
15729
window->Viewport = viewport;
15730
}
15731
15732
static void ScaleWindow(ImGuiWindow* window, float scale)
15733
{
15734
ImVec2 origin = window->Viewport->Pos;
15735
window->Pos = ImFloor((window->Pos - origin) * scale + origin);
15736
window->Size = ImTrunc(window->Size * scale);
15737
window->SizeFull = ImTrunc(window->SizeFull * scale);
15738
window->ContentSize = ImTrunc(window->ContentSize * scale);
15739
}
15740
15741
// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
15742
void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
15743
{
15744
ImGuiContext& g = *GImGui;
15745
for (ImGuiWindow* window : g.Windows)
15746
if (window->Viewport == viewport)
15747
ScaleWindow(window, scale);
15748
}
15749
15750
// Update viewports and monitor infos
15751
static void ImGui::UpdateViewportsNewFrame()
15752
{
15753
ImGuiContext& g = *GImGui;
15754
IM_ASSERT(g.Viewports.Size == 1);
15755
15756
// Update main viewport with current platform position.
15757
// FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
15758
ImGuiViewportP* main_viewport = g.Viewports[0];
15759
main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
15760
main_viewport->Pos = ImVec2(0.0f, 0.0f);
15761
main_viewport->Size = g.IO.DisplaySize;
15762
main_viewport->FramebufferScale = g.IO.DisplayFramebufferScale;
15763
IM_ASSERT(main_viewport->FramebufferScale.x > 0.0f && main_viewport->FramebufferScale.y > 0.0f);
15764
15765
for (ImGuiViewportP* viewport : g.Viewports)
15766
{
15767
// Lock down space taken by menu bars and status bars
15768
// Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.
15769
viewport->WorkInsetMin = viewport->BuildWorkInsetMin;
15770
viewport->WorkInsetMax = viewport->BuildWorkInsetMax;
15771
viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f);
15772
viewport->UpdateWorkRect();
15773
}
15774
}
15775
15776
//-----------------------------------------------------------------------------
15777
// [SECTION] DOCKING
15778
//-----------------------------------------------------------------------------
15779
15780
// (this section is filled in the 'docking' branch)
15781
15782
15783
//-----------------------------------------------------------------------------
15784
// [SECTION] PLATFORM DEPENDENT HELPERS
15785
//-----------------------------------------------------------------------------
15786
// - Default clipboard handlers
15787
// - Default shell function handlers
15788
// - Default IME handlers
15789
//-----------------------------------------------------------------------------
15790
15791
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
15792
15793
#ifdef _MSC_VER
15794
#pragma comment(lib, "user32")
15795
#pragma comment(lib, "kernel32")
15796
#endif
15797
15798
// Win32 clipboard implementation
15799
// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
15800
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15801
{
15802
ImGuiContext& g = *ctx;
15803
g.ClipboardHandlerData.clear();
15804
if (!::OpenClipboard(NULL))
15805
return NULL;
15806
HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
15807
if (wbuf_handle == NULL)
15808
{
15809
::CloseClipboard();
15810
return NULL;
15811
}
15812
if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
15813
{
15814
int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
15815
g.ClipboardHandlerData.resize(buf_len);
15816
::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
15817
}
15818
::GlobalUnlock(wbuf_handle);
15819
::CloseClipboard();
15820
return g.ClipboardHandlerData.Data;
15821
}
15822
15823
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
15824
{
15825
if (!::OpenClipboard(NULL))
15826
return;
15827
const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
15828
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
15829
if (wbuf_handle == NULL)
15830
{
15831
::CloseClipboard();
15832
return;
15833
}
15834
WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
15835
::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
15836
::GlobalUnlock(wbuf_handle);
15837
::EmptyClipboard();
15838
if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
15839
::GlobalFree(wbuf_handle);
15840
::CloseClipboard();
15841
}
15842
15843
#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
15844
15845
#include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
15846
static PasteboardRef main_clipboard = 0;
15847
15848
// OSX clipboard implementation
15849
// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
15850
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
15851
{
15852
if (!main_clipboard)
15853
PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15854
PasteboardClear(main_clipboard);
15855
CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, ImStrlen(text));
15856
if (cf_data)
15857
{
15858
PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
15859
CFRelease(cf_data);
15860
}
15861
}
15862
15863
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15864
{
15865
ImGuiContext& g = *ctx;
15866
if (!main_clipboard)
15867
PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15868
PasteboardSynchronize(main_clipboard);
15869
15870
ItemCount item_count = 0;
15871
PasteboardGetItemCount(main_clipboard, &item_count);
15872
for (ItemCount i = 0; i < item_count; i++)
15873
{
15874
PasteboardItemID item_id = 0;
15875
PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
15876
CFArrayRef flavor_type_array = 0;
15877
PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
15878
for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
15879
{
15880
CFDataRef cf_data;
15881
if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
15882
{
15883
g.ClipboardHandlerData.clear();
15884
int length = (int)CFDataGetLength(cf_data);
15885
g.ClipboardHandlerData.resize(length + 1);
15886
CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
15887
g.ClipboardHandlerData[length] = 0;
15888
CFRelease(cf_data);
15889
return g.ClipboardHandlerData.Data;
15890
}
15891
}
15892
}
15893
return NULL;
15894
}
15895
15896
#else
15897
15898
// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
15899
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15900
{
15901
ImGuiContext& g = *ctx;
15902
return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
15903
}
15904
15905
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text)
15906
{
15907
ImGuiContext& g = *ctx;
15908
g.ClipboardHandlerData.clear();
15909
const char* text_end = text + ImStrlen(text);
15910
g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
15911
memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
15912
g.ClipboardHandlerData[(int)(text_end - text)] = 0;
15913
}
15914
15915
#endif // Default clipboard handlers
15916
15917
//-----------------------------------------------------------------------------
15918
15919
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15920
#if defined(__APPLE__) && TARGET_OS_IPHONE
15921
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15922
#endif
15923
#if defined(__3DS__)
15924
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15925
#endif
15926
#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
15927
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15928
#endif
15929
#endif // #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15930
15931
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15932
#ifdef _WIN32
15933
#include <shellapi.h> // ShellExecuteA()
15934
#ifdef _MSC_VER
15935
#pragma comment(lib, "shell32")
15936
#endif
15937
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
15938
{
15939
const int path_wsize = ::MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
15940
ImVector<wchar_t> path_wbuf;
15941
path_wbuf.resize(path_wsize);
15942
::MultiByteToWideChar(CP_UTF8, 0, path, -1, path_wbuf.Data, path_wsize);
15943
return (INT_PTR)::ShellExecuteW(NULL, L"open", path_wbuf.Data, NULL, NULL, SW_SHOWDEFAULT) > 32;
15944
}
15945
#else
15946
#include <sys/wait.h>
15947
#include <unistd.h>
15948
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
15949
{
15950
#if defined(__APPLE__)
15951
const char* args[] { "open", "--", path, NULL };
15952
#else
15953
const char* args[] { "xdg-open", path, NULL };
15954
#endif
15955
pid_t pid = fork();
15956
if (pid < 0)
15957
return false;
15958
if (!pid)
15959
{
15960
execvp(args[0], const_cast<char **>(args));
15961
exit(-1);
15962
}
15963
else
15964
{
15965
int status;
15966
waitpid(pid, &status, 0);
15967
return WEXITSTATUS(status) == 0;
15968
}
15969
}
15970
#endif
15971
#else
15972
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }
15973
#endif // Default shell handlers
15974
15975
//-----------------------------------------------------------------------------
15976
15977
// Win32 API IME support (for Asian languages, etc.)
15978
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
15979
15980
#include <imm.h>
15981
#ifdef _MSC_VER
15982
#pragma comment(lib, "imm32")
15983
#endif
15984
15985
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
15986
{
15987
// Notify OS Input Method Editor of text input position
15988
HWND hwnd = (HWND)viewport->PlatformHandleRaw;
15989
if (hwnd == 0)
15990
return;
15991
15992
//::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
15993
if (HIMC himc = ::ImmGetContext(hwnd))
15994
{
15995
COMPOSITIONFORM composition_form = {};
15996
composition_form.ptCurrentPos.x = (LONG)data->InputPos.x;
15997
composition_form.ptCurrentPos.y = (LONG)data->InputPos.y;
15998
composition_form.dwStyle = CFS_FORCE_POSITION;
15999
::ImmSetCompositionWindow(himc, &composition_form);
16000
CANDIDATEFORM candidate_form = {};
16001
candidate_form.dwStyle = CFS_CANDIDATEPOS;
16002
candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x;
16003
candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y;
16004
::ImmSetCandidateWindow(himc, &candidate_form);
16005
::ImmReleaseContext(hwnd, himc);
16006
}
16007
}
16008
16009
#else
16010
16011
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}
16012
16013
#endif // Default IME handlers
16014
16015
//-----------------------------------------------------------------------------
16016
// [SECTION] METRICS/DEBUGGER WINDOW
16017
//-----------------------------------------------------------------------------
16018
// - MetricsHelpMarker() [Internal]
16019
// - DebugRenderViewportThumbnail() [Internal]
16020
// - RenderViewportsThumbnails() [Internal]
16021
// - DebugRenderKeyboardPreview() [Internal]
16022
// - DebugTextEncoding()
16023
// - DebugFlashStyleColorStop() [Internal]
16024
// - DebugFlashStyleColor()
16025
// - UpdateDebugToolFlashStyleColor() [Internal]
16026
// - ShowFontAtlas() [Internal but called by Demo!]
16027
// - DebugNodeTexture() [Internal]
16028
// - ShowMetricsWindow()
16029
// - DebugNodeColumns() [Internal]
16030
// - DebugNodeDrawList() [Internal]
16031
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
16032
// - DebugNodeFont() [Internal]
16033
// - DebugNodeFontGlyph() [Internal]
16034
// - DebugNodeStorage() [Internal]
16035
// - DebugNodeTabBar() [Internal]
16036
// - DebugNodeViewport() [Internal]
16037
// - DebugNodeWindow() [Internal]
16038
// - DebugNodeWindowSettings() [Internal]
16039
// - DebugNodeWindowsList() [Internal]
16040
// - DebugNodeWindowsListByBeginStackParent() [Internal]
16041
// - ShowFontSelector()
16042
//-----------------------------------------------------------------------------
16043
16044
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
16045
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
16046
static void MetricsHelpMarker(const char* desc)
16047
{
16048
ImGui::TextDisabled("(?)");
16049
if (ImGui::BeginItemTooltip())
16050
{
16051
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
16052
ImGui::TextUnformatted(desc);
16053
ImGui::PopTextWrapPos();
16054
ImGui::EndTooltip();
16055
}
16056
}
16057
#endif
16058
16059
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
16060
16061
void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
16062
{
16063
ImGuiContext& g = *GImGui;
16064
ImGuiWindow* window = g.CurrentWindow;
16065
16066
ImVec2 scale = bb.GetSize() / viewport->Size;
16067
ImVec2 off = bb.Min - viewport->Pos * scale;
16068
float alpha_mul = 1.0f;
16069
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
16070
for (ImGuiWindow* thumb_window : g.Windows)
16071
{
16072
if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
16073
continue;
16074
16075
ImRect thumb_r = thumb_window->Rect();
16076
ImRect title_r = thumb_window->TitleBarRect();
16077
thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale));
16078
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
16079
thumb_r.ClipWithFull(bb);
16080
title_r.ClipWithFull(bb);
16081
const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
16082
window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
16083
window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
16084
window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
16085
window->DrawList->AddText(g.Font, g.FontSize * 1.0f, g.FontWeight, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
16086
}
16087
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
16088
if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
16089
window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
16090
}
16091
16092
static void RenderViewportsThumbnails()
16093
{
16094
ImGuiContext& g = *GImGui;
16095
ImGuiWindow* window = g.CurrentWindow;
16096
16097
float SCALE = 1.0f / 8.0f;
16098
ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size);
16099
ImVec2 p = window->DC.CursorPos;
16100
ImVec2 off = p - bb_full.Min * SCALE;
16101
16102
// Draw viewports
16103
for (ImGuiViewportP* viewport : g.Viewports)
16104
{
16105
ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
16106
ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
16107
}
16108
ImGui::Dummy(bb_full.GetSize() * SCALE);
16109
}
16110
16111
// Draw an arbitrary US keyboard layout to visualize translated keys
16112
void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
16113
{
16114
const float scale = ImGui::GetFontSize() / 13.0f;
16115
const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
16116
const float key_rounding = 3.0f * scale;
16117
const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
16118
const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
16119
const float key_face_rounding = 2.0f * scale;
16120
const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
16121
const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
16122
const float key_row_offset = 9.0f * scale;
16123
16124
ImVec2 board_min = GetCursorScreenPos();
16125
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);
16126
ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
16127
16128
struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
16129
const KeyLayoutData keys_to_display[] =
16130
{
16131
{ 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
16132
{ 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
16133
{ 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
16134
};
16135
16136
// Elements rendered manually via ImDrawList API are not clipped automatically.
16137
// While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
16138
Dummy(board_max - board_min);
16139
if (!IsItemVisible())
16140
return;
16141
draw_list->PushClipRect(board_min, board_max, true);
16142
for (int n = 0; n < IM_COUNTOF(keys_to_display); n++)
16143
{
16144
const KeyLayoutData* key_data = &keys_to_display[n];
16145
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);
16146
ImVec2 key_max = key_min + key_size;
16147
draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
16148
draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
16149
ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
16150
ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
16151
draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
16152
draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
16153
ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
16154
draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
16155
if (IsKeyDown(key_data->Key))
16156
draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
16157
}
16158
draw_list->PopClipRect();
16159
}
16160
16161
// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
16162
void ImGui::DebugTextEncoding(const char* str)
16163
{
16164
Text("Text: \"%s\"", str);
16165
if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
16166
return;
16167
TableSetupColumn("Offset");
16168
TableSetupColumn("UTF-8");
16169
TableSetupColumn("Glyph");
16170
TableSetupColumn("Codepoint");
16171
TableHeadersRow();
16172
const char* str_end = str + strlen(str); // As we may receive malformed UTF-8, pass an explicit end instead of relying on ImTextCharFromUtf8() assuming enough space.
16173
for (const char* p = str; *p != 0; )
16174
{
16175
unsigned int c;
16176
const int c_utf8_len = ImTextCharFromUtf8(&c, p, str_end);
16177
TableNextColumn();
16178
Text("%d", (int)(p - str));
16179
TableNextColumn();
16180
for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
16181
{
16182
if (byte_index > 0)
16183
SameLine();
16184
Text("0x%02X", (int)(unsigned char)p[byte_index]);
16185
}
16186
TableNextColumn();
16187
TextUnformatted(p, p + c_utf8_len);
16188
if (!GetFont()->IsGlyphInFont((ImWchar)c))
16189
{
16190
SameLine();
16191
TextUnformatted("[missing]");
16192
}
16193
TableNextColumn();
16194
Text("U+%04X", (int)c);
16195
p += c_utf8_len;
16196
}
16197
EndTable();
16198
}
16199
16200
static void DebugFlashStyleColorStop()
16201
{
16202
ImGuiContext& g = *GImGui;
16203
if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
16204
g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
16205
g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
16206
}
16207
16208
// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
16209
void ImGui::DebugFlashStyleColor(ImGuiCol idx)
16210
{
16211
ImGuiContext& g = *GImGui;
16212
DebugFlashStyleColorStop();
16213
g.DebugFlashStyleColorTime = 0.5f;
16214
g.DebugFlashStyleColorIdx = idx;
16215
g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
16216
}
16217
16218
void ImGui::UpdateDebugToolFlashStyleColor()
16219
{
16220
ImGuiContext& g = *GImGui;
16221
if (g.DebugFlashStyleColorTime <= 0.0f)
16222
return;
16223
ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);
16224
g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
16225
if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
16226
DebugFlashStyleColorStop();
16227
}
16228
16229
ImU64 ImGui::DebugTextureIDToU64(ImTextureID tex_id)
16230
{
16231
ImU64 v = 0;
16232
memcpy(&v, &tex_id, ImMin(sizeof(ImU64), sizeof(ImTextureID)));
16233
return v;
16234
}
16235
16236
static const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref)
16237
{
16238
char* buf_p = buf;
16239
char* buf_end = buf + buf_size;
16240
if (tex_ref._TexData != NULL)
16241
buf_p += ImFormatString(buf_p, buf_end - buf_p, "#%03d: ", tex_ref._TexData->UniqueID);
16242
ImFormatString(buf_p, buf_end - buf_p, "0x%X", ImGui::DebugTextureIDToU64(tex_ref.GetTexID()));
16243
return buf;
16244
}
16245
16246
#ifdef IMGUI_ENABLE_FREETYPE
16247
namespace ImGuiFreeType { IMGUI_API const ImFontLoader* GetFontLoader(); IMGUI_API bool DebugEditFontLoaderFlags(unsigned int* p_font_builder_flags); }
16248
#endif
16249
16250
// [DEBUG] List fonts in a font atlas and display its texture
16251
void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
16252
{
16253
ImGuiContext& g = *GImGui;
16254
ImGuiIO& io = g.IO;
16255
ImGuiStyle& style = g.Style;
16256
16257
BeginDisabled();
16258
CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
16259
EndDisabled();
16260
ShowFontSelector("Font");
16261
//BeginDisabled((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0);
16262
if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
16263
style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
16264
SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
16265
SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later.");
16266
DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);
16267
//BeginDisabled(io.ConfigDpiScaleFonts);
16268
DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f);
16269
//SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
16270
//EndDisabled();
16271
if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
16272
{
16273
BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
16274
BulletText("For instructions, see:");
16275
SameLine();
16276
TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md");
16277
}
16278
BulletText("Load a nice font for better results!");
16279
BulletText("Please submit feedback:");
16280
SameLine(); TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465");
16281
BulletText("Read FAQ for more details:");
16282
SameLine(); TextLinkOpenURL("dearimgui.com/faq", "https://www.dearimgui.com/faq/");
16283
//EndDisabled();
16284
16285
SeparatorText("Font List");
16286
16287
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16288
Checkbox("Show font preview", &cfg->ShowFontPreview);
16289
16290
// Font loaders
16291
if (TreeNode("Loader", "Loader: \'%s\'", atlas->FontLoaderName ? atlas->FontLoaderName : "NULL"))
16292
{
16293
const ImFontLoader* loader_current = atlas->FontLoader;
16294
BeginDisabled(!atlas->RendererHasTextures);
16295
#ifdef IMGUI_ENABLE_STB_TRUETYPE
16296
const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype();
16297
if (RadioButton("stb_truetype", loader_current == loader_stbtruetype))
16298
atlas->SetFontLoader(loader_stbtruetype);
16299
#else
16300
BeginDisabled();
16301
RadioButton("stb_truetype", false);
16302
SetItemTooltip("Requires #define IMGUI_ENABLE_STB_TRUETYPE");
16303
EndDisabled();
16304
#endif
16305
SameLine();
16306
#ifdef IMGUI_ENABLE_FREETYPE
16307
const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader();
16308
if (RadioButton("FreeType", loader_current == loader_freetype))
16309
atlas->SetFontLoader(loader_freetype);
16310
if (loader_current == loader_freetype)
16311
{
16312
unsigned int loader_flags = atlas->FontLoaderFlags;
16313
Text("Shared FreeType Loader Flags: 0x%08X", loader_flags);
16314
if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))
16315
{
16316
for (ImFont* font : atlas->Fonts)
16317
ImFontAtlasFontDestroyOutput(atlas, font);
16318
atlas->FontLoaderFlags = loader_flags;
16319
for (ImFont* font : atlas->Fonts)
16320
ImFontAtlasFontInitOutput(atlas, font);
16321
}
16322
}
16323
#else
16324
BeginDisabled();
16325
RadioButton("FreeType", false);
16326
SetItemTooltip("Requires #define IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp.");
16327
EndDisabled();
16328
#endif
16329
EndDisabled();
16330
TreePop();
16331
}
16332
16333
// Font list
16334
for (ImFont* font : atlas->Fonts)
16335
{
16336
PushID(font);
16337
DebugNodeFont(font);
16338
PopID();
16339
}
16340
16341
SeparatorText("Font Atlas");
16342
if (Button("Compact"))
16343
atlas->CompactCache();
16344
SameLine();
16345
if (Button("Grow"))
16346
ImFontAtlasTextureGrow(atlas);
16347
SameLine();
16348
if (Button("Clear All"))
16349
ImFontAtlasBuildClear(atlas);
16350
SetItemTooltip("Destroy cache and custom rectangles.");
16351
16352
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
16353
{
16354
ImTextureData* tex = atlas->TexList[tex_n];
16355
if (tex_n > 0)
16356
SameLine();
16357
Text("Tex: %dx%d", tex->Width, tex->Height);
16358
}
16359
const int packed_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsPackedSurface);
16360
const int discarded_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsDiscardedSurface);
16361
Text("Packed rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsPackedCount, atlas->Builder->RectsPackedSurface, packed_surface_sqrt, packed_surface_sqrt);
16362
Text("incl. Discarded rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsDiscardedCount, atlas->Builder->RectsDiscardedSurface, discarded_surface_sqrt, discarded_surface_sqrt);
16363
16364
ImFontAtlasRectId highlight_r_id = ImFontAtlasRectId_Invalid;
16365
if (TreeNode("Rects Index", "Rects Index (%d)", atlas->Builder->RectsPackedCount)) // <-- Use count of used rectangles
16366
{
16367
PushStyleVar(ImGuiStyleVar_ImageBorderSize, 1.0f);
16368
if (BeginTable("##table", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY, ImVec2(0.0f, GetTextLineHeightWithSpacing() * 12)))
16369
{
16370
for (const ImFontAtlasRectEntry& entry : atlas->Builder->RectsIndex)
16371
if (entry.IsUsed)
16372
{
16373
ImFontAtlasRectId id = ImFontAtlasRectId_Make(atlas->Builder->RectsIndex.index_from_ptr(&entry), entry.Generation);
16374
ImFontAtlasRect r = {};
16375
atlas->GetCustomRect(id, &r);
16376
const char* buf;
16377
ImFormatStringToTempBuffer(&buf, NULL, "ID:%08X, used:%d, { w:%3d, h:%3d } { x:%4d, y:%4d }", id, entry.IsUsed, r.w, r.h, r.x, r.y);
16378
TableNextColumn();
16379
Selectable(buf);
16380
if (IsItemHovered())
16381
highlight_r_id = id;
16382
TableNextColumn();
16383
Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);
16384
}
16385
EndTable();
16386
}
16387
PopStyleVar();
16388
TreePop();
16389
}
16390
16391
// Texture list
16392
// (ensure the last texture always use the same ID, so we can keep it open neatly)
16393
ImFontAtlasRect highlight_r;
16394
if (highlight_r_id != ImFontAtlasRectId_Invalid)
16395
atlas->GetCustomRect(highlight_r_id, &highlight_r);
16396
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
16397
{
16398
if (tex_n == atlas->TexList.Size - 1)
16399
SetNextItemOpen(true, ImGuiCond_Once);
16400
DebugNodeTexture(atlas->TexList[tex_n], atlas->TexList.Size - 1 - tex_n, (highlight_r_id != ImFontAtlasRectId_Invalid) ? &highlight_r : NULL);
16401
}
16402
}
16403
16404
void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect)
16405
{
16406
ImGuiContext& g = *GImGui;
16407
PushID(int_id);
16408
if (TreeNode("", "Texture #%03d (%dx%d pixels)", tex->UniqueID, tex->Width, tex->Height))
16409
{
16410
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16411
Checkbox("Show used rect", &cfg->ShowTextureUsedRect);
16412
PushStyleVar(ImGuiStyleVar_ImageBorderSize, ImMax(1.0f, g.Style.ImageBorderSize));
16413
ImVec2 p = GetCursorScreenPos();
16414
if (tex->WantDestroyNextFrame)
16415
Dummy(ImVec2((float)tex->Width, (float)tex->Height));
16416
else
16417
ImageWithBg(tex->GetTexRef(), ImVec2((float)tex->Width, (float)tex->Height), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
16418
if (cfg->ShowTextureUsedRect)
16419
GetWindowDrawList()->AddRect(ImVec2(p.x + tex->UsedRect.x, p.y + tex->UsedRect.y), ImVec2(p.x + tex->UsedRect.x + tex->UsedRect.w, p.y + tex->UsedRect.y + tex->UsedRect.h), IM_COL32(255, 0, 255, 255));
16420
if (highlight_rect != NULL)
16421
{
16422
ImRect r_outer(p.x, p.y, p.x + tex->Width, p.y + tex->Height);
16423
ImRect r_inner(p.x + highlight_rect->x, p.y + highlight_rect->y, p.x + highlight_rect->x + highlight_rect->w, p.y + highlight_rect->y + highlight_rect->h);
16424
RenderRectFilledWithHole(GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0f);
16425
GetWindowDrawList()->AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255));
16426
}
16427
PopStyleVar();
16428
16429
char texref_desc[30];
16430
Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors);
16431
Text("TexRef = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texref_desc, IM_COUNTOF(texref_desc), tex->GetTexRef()), tex->BackendUserData);
16432
TreePop();
16433
}
16434
PopID();
16435
}
16436
16437
void ImGui::ShowMetricsWindow(bool* p_open)
16438
{
16439
ImGuiContext& g = *GImGui;
16440
ImGuiIO& io = g.IO;
16441
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16442
if (cfg->ShowDebugLog)
16443
ShowDebugLogWindow(&cfg->ShowDebugLog);
16444
if (cfg->ShowIDStackTool)
16445
ShowIDStackToolWindow(&cfg->ShowIDStackTool);
16446
16447
if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
16448
{
16449
End();
16450
return;
16451
}
16452
16453
// [DEBUG] Clear debug breaks hooks after exactly one cycle.
16454
DebugBreakClearData();
16455
16456
// Basic info
16457
Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
16458
if (g.ContextName[0] != 0)
16459
{
16460
SameLine();
16461
Text("(Context Name: \"%s\")", g.ContextName);
16462
}
16463
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
16464
Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
16465
Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
16466
//SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
16467
16468
Separator();
16469
16470
// Debugging enums
16471
enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
16472
const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
16473
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
16474
const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
16475
if (cfg->ShowWindowsRectsType < 0)
16476
cfg->ShowWindowsRectsType = WRT_WorkRect;
16477
if (cfg->ShowTablesRectsType < 0)
16478
cfg->ShowTablesRectsType = TRT_WorkRect;
16479
16480
struct Funcs
16481
{
16482
static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
16483
{
16484
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
16485
if (rect_type == TRT_OuterRect) { return table->OuterRect; }
16486
else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
16487
else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
16488
else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
16489
else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
16490
else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
16491
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); }
16492
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); }
16493
else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
16494
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
16495
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); }
16496
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); }
16497
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); }
16498
IM_ASSERT(0);
16499
return ImRect();
16500
}
16501
16502
static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
16503
{
16504
if (rect_type == WRT_OuterRect) { return window->Rect(); }
16505
else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
16506
else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
16507
else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
16508
else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
16509
else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
16510
else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
16511
else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
16512
IM_ASSERT(0);
16513
return ImRect();
16514
}
16515
};
16516
16517
#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
16518
TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n");
16519
#endif
16520
16521
// Tools
16522
if (TreeNode("Tools"))
16523
{
16524
// Debug Break features
16525
// The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
16526
SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
16527
SameLine();
16528
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.");
16529
if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
16530
DebugStartItemPicker();
16531
Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
16532
16533
SeparatorText("Visualize");
16534
16535
Checkbox("Show Debug Log", &cfg->ShowDebugLog);
16536
SameLine();
16537
MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
16538
16539
Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
16540
SameLine();
16541
MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
16542
16543
Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
16544
Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
16545
SameLine();
16546
SetNextItemWidth(GetFontSize() * 12);
16547
cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
16548
if (cfg->ShowWindowsRects && g.NavWindow != NULL)
16549
{
16550
BulletText("'%s':", g.NavWindow->Name);
16551
Indent();
16552
for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
16553
{
16554
ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
16555
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]);
16556
}
16557
Unindent();
16558
}
16559
16560
Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
16561
SameLine();
16562
SetNextItemWidth(GetFontSize() * 12);
16563
cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
16564
if (cfg->ShowTablesRects && g.NavWindow != NULL)
16565
{
16566
for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16567
{
16568
ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16569
if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
16570
continue;
16571
16572
BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
16573
if (IsItemHovered())
16574
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16575
Indent();
16576
char buf[128];
16577
for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
16578
{
16579
if (rect_n >= TRT_ColumnsRect)
16580
{
16581
if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
16582
continue;
16583
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16584
{
16585
ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
16586
ImFormatString(buf, IM_COUNTOF(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]);
16587
Selectable(buf);
16588
if (IsItemHovered())
16589
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16590
}
16591
}
16592
else
16593
{
16594
ImRect r = Funcs::GetTableRect(table, rect_n, -1);
16595
ImFormatString(buf, IM_COUNTOF(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]);
16596
Selectable(buf);
16597
if (IsItemHovered())
16598
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16599
}
16600
}
16601
Unindent();
16602
}
16603
}
16604
Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
16605
16606
SeparatorText("Validate");
16607
16608
Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
16609
SameLine();
16610
MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
16611
16612
Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
16613
SameLine();
16614
MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
16615
if (cfg->ShowTextEncodingViewer)
16616
{
16617
static char buf[64] = "";
16618
SetNextItemWidth(-FLT_MIN);
16619
InputText("##DebugTextEncodingBuf", buf, IM_COUNTOF(buf));
16620
if (buf[0] != 0)
16621
DebugTextEncoding(buf);
16622
}
16623
16624
TreePop();
16625
}
16626
16627
// Windows
16628
if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
16629
{
16630
//SetNextItemOpen(true, ImGuiCond_Once);
16631
DebugNodeWindowsList(&g.Windows, "By display order");
16632
DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
16633
if (TreeNode("By submission order (begin stack)"))
16634
{
16635
// Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
16636
ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
16637
temp_buffer.resize(0);
16638
for (ImGuiWindow* window : g.Windows)
16639
if (window->LastFrameActive + 1 >= g.FrameCount)
16640
temp_buffer.push_back(window);
16641
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); } };
16642
ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
16643
DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
16644
TreePop();
16645
}
16646
16647
TreePop();
16648
}
16649
16650
// DrawLists
16651
int drawlist_count = 0;
16652
for (ImGuiViewportP* viewport : g.Viewports)
16653
drawlist_count += viewport->DrawDataP.CmdLists.Size;
16654
if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
16655
{
16656
Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
16657
Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
16658
for (ImGuiViewportP* viewport : g.Viewports)
16659
for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
16660
DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
16661
TreePop();
16662
}
16663
16664
// Viewports
16665
if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
16666
{
16667
SetNextItemOpen(true, ImGuiCond_Once);
16668
if (TreeNode("Windows Minimap"))
16669
{
16670
RenderViewportsThumbnails();
16671
TreePop();
16672
}
16673
cfg->HighlightViewportID = 0;
16674
16675
for (ImGuiViewportP* viewport : g.Viewports)
16676
DebugNodeViewport(viewport);
16677
TreePop();
16678
}
16679
16680
// Details for Fonts
16681
for (ImFontAtlas* atlas : g.FontAtlases)
16682
if (TreeNode((void*)atlas, "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size))
16683
{
16684
ShowFontAtlas(atlas);
16685
TreePop();
16686
}
16687
16688
// Details for Popups
16689
if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
16690
{
16691
for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
16692
{
16693
// As it's difficult to interact with tree nodes while popups are open, we display everything inline.
16694
ImGuiWindow* window = popup_data.Window;
16695
BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
16696
popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
16697
popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
16698
}
16699
TreePop();
16700
}
16701
16702
// Details for TabBars
16703
if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
16704
{
16705
for (int n = 0; n < g.TabBars.GetMapSize(); n++)
16706
if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
16707
{
16708
PushID(tab_bar);
16709
DebugNodeTabBar(tab_bar, "TabBar");
16710
PopID();
16711
}
16712
TreePop();
16713
}
16714
16715
// Details for Tables
16716
if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
16717
{
16718
for (int n = 0; n < g.Tables.GetMapSize(); n++)
16719
if (ImGuiTable* table = g.Tables.TryGetMapData(n))
16720
DebugNodeTable(table);
16721
TreePop();
16722
}
16723
16724
// Details for InputText
16725
if (TreeNode("InputText"))
16726
{
16727
DebugNodeInputTextState(&g.InputTextState);
16728
TreePop();
16729
}
16730
16731
// Details for TypingSelect
16732
if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
16733
{
16734
DebugNodeTypingSelectState(&g.TypingSelectState);
16735
TreePop();
16736
}
16737
16738
// Details for MultiSelect
16739
if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
16740
{
16741
ImGuiBoxSelectState* bs = &g.BoxSelectState;
16742
BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
16743
for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
16744
if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
16745
DebugNodeMultiSelectState(state);
16746
TreePop();
16747
}
16748
16749
// Details for Docking
16750
#ifdef IMGUI_HAS_DOCK
16751
if (TreeNode("Docking"))
16752
{
16753
TreePop();
16754
}
16755
#endif // #ifdef IMGUI_HAS_DOCK
16756
16757
// Settings
16758
if (TreeNode("Settings"))
16759
{
16760
if (SmallButton("Clear"))
16761
ClearIniSettings();
16762
SameLine();
16763
if (SmallButton("Save to memory"))
16764
SaveIniSettingsToMemory();
16765
SameLine();
16766
if (SmallButton("Save to disk"))
16767
SaveIniSettingsToDisk(g.IO.IniFilename);
16768
SameLine();
16769
if (g.IO.IniFilename)
16770
Text("\"%s\"", g.IO.IniFilename);
16771
else
16772
TextUnformatted("<NULL>");
16773
Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
16774
Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
16775
if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
16776
{
16777
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
16778
BulletText("\"%s\"", handler.TypeName);
16779
TreePop();
16780
}
16781
if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
16782
{
16783
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16784
DebugNodeWindowSettings(settings);
16785
TreePop();
16786
}
16787
16788
if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
16789
{
16790
for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
16791
DebugNodeTableSettings(settings);
16792
TreePop();
16793
}
16794
16795
#ifdef IMGUI_HAS_DOCK
16796
#endif // #ifdef IMGUI_HAS_DOCK
16797
16798
if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
16799
{
16800
InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
16801
TreePop();
16802
}
16803
TreePop();
16804
}
16805
16806
// Settings
16807
if (TreeNode("Memory allocations"))
16808
{
16809
ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
16810
Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
16811
if (SmallButton("GC now")) { g.GcCompactAll = true; }
16812
Text("Recent frames with allocations:");
16813
int buf_size = IM_COUNTOF(info->LastEntriesBuf);
16814
for (int n = buf_size - 1; n >= 0; n--)
16815
{
16816
ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
16817
BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);
16818
if (n == 0)
16819
{
16820
SameLine();
16821
Text("<- %d frames ago", g.FrameCount - entry->FrameCount);
16822
}
16823
}
16824
TreePop();
16825
}
16826
16827
if (TreeNode("Inputs"))
16828
{
16829
Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
16830
{
16831
// User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
16832
Indent();
16833
Text("Keys down:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
16834
Text("Keys pressed:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
16835
Text("Keys released:"); for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
16836
Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "Ctrl " : "", io.KeyShift ? "Shift " : "", io.KeyAlt ? "Alt " : "", io.KeySuper ? "Super " : "");
16837
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.
16838
DebugRenderKeyboardPreview(GetWindowDrawList());
16839
Unindent();
16840
}
16841
16842
Text("MOUSE STATE");
16843
{
16844
Indent();
16845
if (IsMousePosValid())
16846
Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
16847
else
16848
Text("Mouse pos: <INVALID>");
16849
Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
16850
int count = IM_COUNTOF(io.MouseDown);
16851
Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
16852
Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
16853
Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
16854
Text("Mouse wheel: %.1f", io.MouseWheel);
16855
Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
16856
Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
16857
Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
16858
Unindent();
16859
}
16860
16861
Text("MOUSE WHEELING");
16862
{
16863
Indent();
16864
Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
16865
Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
16866
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>");
16867
Unindent();
16868
}
16869
16870
Text("KEY OWNERS");
16871
{
16872
Indent();
16873
if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
16874
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
16875
{
16876
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
16877
if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
16878
continue;
16879
Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
16880
owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
16881
DebugLocateItemOnHover(owner_data->OwnerCurr);
16882
}
16883
EndChild();
16884
Unindent();
16885
}
16886
Text("SHORTCUT ROUTING");
16887
SameLine();
16888
MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
16889
{
16890
Indent();
16891
if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
16892
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
16893
{
16894
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
16895
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
16896
{
16897
ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
16898
ImGuiKeyChord key_chord = key | routing_data->Mods;
16899
Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
16900
DebugLocateItemOnHover(routing_data->RoutingCurr);
16901
if (g.IO.ConfigDebugIsDebuggerPresent)
16902
{
16903
SameLine();
16904
if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
16905
g.DebugBreakInShortcutRouting = key_chord;
16906
}
16907
idx = routing_data->NextEntryIndex;
16908
}
16909
}
16910
EndChild();
16911
Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
16912
Unindent();
16913
}
16914
TreePop();
16915
}
16916
16917
if (TreeNode("Internal state"))
16918
{
16919
Text("WINDOWING");
16920
Indent();
16921
Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
16922
Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
16923
Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
16924
Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
16925
Unindent();
16926
16927
Text("ITEMS");
16928
Indent();
16929
Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
16930
DebugLocateItemOnHover(g.ActiveId);
16931
Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
16932
Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
16933
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
16934
Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
16935
Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
16936
DebugLocateItemOnHover(g.DragDropPayload.SourceId);
16937
Unindent();
16938
16939
Text("NAV,FOCUS");
16940
Indent();
16941
Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
16942
Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
16943
DebugLocateItemOnHover(g.NavId);
16944
Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
16945
Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
16946
Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
16947
Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
16948
Text("NavActivateFlags: %04X", g.NavActivateFlags);
16949
Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav);
16950
Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
16951
Text("NavFocusRoute[] = ");
16952
for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
16953
{
16954
const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
16955
SameLine(0.0f, 0.0f);
16956
Text("0x%08X/", focus_scope.ID);
16957
SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
16958
}
16959
Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
16960
Unindent();
16961
16962
TreePop();
16963
}
16964
16965
// Overlay: Display windows Rectangles and Begin Order
16966
if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
16967
{
16968
for (ImGuiWindow* window : g.Windows)
16969
{
16970
if (!window->WasActive)
16971
continue;
16972
ImDrawList* draw_list = GetForegroundDrawList(window);
16973
if (cfg->ShowWindowsRects)
16974
{
16975
ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
16976
draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16977
}
16978
if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
16979
{
16980
char buf[32];
16981
ImFormatString(buf, IM_COUNTOF(buf), "%d", window->BeginOrderWithinContext);
16982
float font_size = GetFontSize();
16983
draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
16984
draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
16985
}
16986
}
16987
}
16988
16989
// Overlay: Display Tables Rectangles
16990
if (cfg->ShowTablesRects)
16991
{
16992
for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16993
{
16994
ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16995
if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
16996
continue;
16997
ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
16998
if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
16999
{
17000
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
17001
{
17002
ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
17003
ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
17004
float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
17005
draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
17006
}
17007
}
17008
else
17009
{
17010
ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
17011
draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
17012
}
17013
}
17014
}
17015
17016
#ifdef IMGUI_HAS_DOCK
17017
// Overlay: Display Docking info
17018
if (show_docking_nodes && g.IO.KeyCtrl)
17019
{
17020
}
17021
#endif // #ifdef IMGUI_HAS_DOCK
17022
17023
End();
17024
}
17025
17026
void ImGui::DebugBreakClearData()
17027
{
17028
// Those fields are scattered in their respective subsystem to stay in hot-data locations
17029
ImGuiContext& g = *GImGui;
17030
g.DebugBreakInWindow = 0;
17031
g.DebugBreakInTable = 0;
17032
g.DebugBreakInShortcutRouting = ImGuiKey_None;
17033
}
17034
17035
void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
17036
{
17037
if (!BeginItemTooltip())
17038
return;
17039
Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
17040
Separator();
17041
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.");
17042
Separator();
17043
TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
17044
EndTooltip();
17045
}
17046
17047
// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
17048
// In order to reduce interferences with the contents we are trying to debug into.
17049
bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
17050
{
17051
ImGuiWindow* window = GetCurrentWindow();
17052
if (window->SkipItems)
17053
return false;
17054
17055
ImGuiContext& g = *GImGui;
17056
const ImGuiID id = window->GetID(label);
17057
const ImVec2 label_size = CalcTextSize(label, NULL, true);
17058
ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
17059
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
17060
17061
const ImRect bb(pos, pos + size);
17062
ItemSize(size, 0.0f);
17063
if (!ItemAdd(bb, id))
17064
return false;
17065
17066
// WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
17067
bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
17068
bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
17069
DebugBreakButtonTooltip(false, description_of_location);
17070
17071
ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
17072
ImVec4 hsv;
17073
ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
17074
ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
17075
17076
RenderNavCursor(bb, id);
17077
RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
17078
RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
17079
17080
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
17081
return pressed;
17082
}
17083
17084
// [DEBUG] Display contents of Columns
17085
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
17086
{
17087
if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
17088
return;
17089
BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
17090
for (ImGuiOldColumnData& column : columns->Columns)
17091
BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
17092
TreePop();
17093
}
17094
17095
// [DEBUG] Display contents of ImDrawList
17096
void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
17097
{
17098
ImGuiContext& g = *GImGui;
17099
IM_UNUSED(viewport); // Used in docking branch
17100
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
17101
int cmd_count = draw_list->CmdBuffer.Size;
17102
if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
17103
cmd_count--;
17104
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);
17105
if (draw_list == GetWindowDrawList())
17106
{
17107
SameLine();
17108
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)
17109
if (node_open)
17110
TreePop();
17111
return;
17112
}
17113
17114
ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
17115
if (window && IsItemHovered() && fg_draw_list)
17116
fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
17117
if (!node_open)
17118
return;
17119
17120
if (window && !window->WasActive)
17121
TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
17122
17123
for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
17124
{
17125
if (pcmd->UserCallback)
17126
{
17127
BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
17128
continue;
17129
}
17130
17131
char texid_desc[30];
17132
FormatTextureRefForDebugDisplay(texid_desc, IM_COUNTOF(texid_desc), pcmd->TexRef);
17133
char buf[300];
17134
ImFormatString(buf, IM_COUNTOF(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
17135
pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
17136
bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
17137
if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
17138
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
17139
if (!pcmd_node_open)
17140
continue;
17141
17142
// Calculate approximate coverage area (touched pixel count)
17143
// This will be in pixels squared as long there's no post-scaling happening to the renderer output.
17144
const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
17145
const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
17146
float total_area = 0.0f;
17147
for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
17148
{
17149
ImVec2 triangle[3];
17150
for (int n = 0; n < 3; n++, idx_n++)
17151
triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
17152
total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
17153
}
17154
17155
// Display vertex information summary. Hover to get all triangles drawn in wire-frame
17156
ImFormatString(buf, IM_COUNTOF(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
17157
Selectable(buf);
17158
if (IsItemHovered() && fg_draw_list)
17159
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
17160
17161
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
17162
ImGuiListClipper clipper;
17163
clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
17164
while (clipper.Step())
17165
for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
17166
{
17167
char* buf_p = buf, * buf_end = buf + IM_COUNTOF(buf);
17168
ImVec2 triangle[3];
17169
for (int n = 0; n < 3; n++, idx_i++)
17170
{
17171
const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
17172
triangle[n] = v.pos;
17173
buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
17174
(n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
17175
}
17176
17177
Selectable(buf, false);
17178
if (fg_draw_list && IsItemHovered())
17179
{
17180
ImDrawListFlags backup_flags = fg_draw_list->Flags;
17181
fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
17182
fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
17183
fg_draw_list->Flags = backup_flags;
17184
}
17185
}
17186
TreePop();
17187
}
17188
TreePop();
17189
}
17190
17191
// [DEBUG] Display mesh/aabb of a ImDrawCmd
17192
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
17193
{
17194
IM_ASSERT(show_mesh || show_aabb);
17195
17196
// Draw wire-frame version of all triangles
17197
ImRect clip_rect = draw_cmd->ClipRect;
17198
ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
17199
ImDrawListFlags backup_flags = out_draw_list->Flags;
17200
out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
17201
for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
17202
{
17203
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
17204
ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
17205
17206
ImVec2 triangle[3];
17207
for (int n = 0; n < 3; n++, idx_n++)
17208
vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
17209
if (show_mesh)
17210
out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
17211
}
17212
// Draw bounding boxes
17213
if (show_aabb)
17214
{
17215
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
17216
out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
17217
}
17218
out_draw_list->Flags = backup_flags;
17219
}
17220
17221
// [DEBUG] Compute mask of inputs with the same codepoint.
17222
static int CalcFontGlyphSrcOverlapMask(ImFontAtlas* atlas, ImFont* font, unsigned int codepoint)
17223
{
17224
int mask = 0, count = 0;
17225
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17226
{
17227
ImFontConfig* src = font->Sources[src_n];
17228
if (!(src->FontLoader ? src->FontLoader : atlas->FontLoader)->FontSrcContainsGlyph(atlas, src, (ImWchar)codepoint))
17229
continue;
17230
mask |= (1 << src_n);
17231
count++;
17232
}
17233
return count > 1 ? mask : 0;
17234
}
17235
17236
// [DEBUG] Display details for a single font, called by ShowStyleEditor().
17237
void ImGui::DebugNodeFont(ImFont* font)
17238
{
17239
ImGuiContext& g = *GImGui;
17240
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
17241
ImFontAtlas* atlas = font->OwnerAtlas;
17242
bool opened = TreeNode(font, "Font: \"%s\": %d sources(s)", font->GetDebugName(), font->Sources.Size);
17243
17244
// Display preview text
17245
if (!opened)
17246
Indent();
17247
Indent();
17248
if (cfg->ShowFontPreview)
17249
{
17250
PushFont(font, 0.0f, -1.0f);
17251
Text("The quick brown fox jumps over the lazy dog");
17252
PopFont();
17253
}
17254
if (!opened)
17255
{
17256
Unindent();
17257
Unindent();
17258
return;
17259
}
17260
if (SmallButton("Set as default"))
17261
GetIO().FontDefault = font;
17262
SameLine();
17263
BeginDisabled(atlas->Fonts.Size <= 1 || atlas->Locked);
17264
if (SmallButton("Remove"))
17265
atlas->RemoveFont(font);
17266
EndDisabled();
17267
SameLine();
17268
if (SmallButton("Clear bakes"))
17269
ImFontAtlasFontDiscardBakes(atlas, font, 0);
17270
SameLine();
17271
if (SmallButton("Clear unused"))
17272
ImFontAtlasFontDiscardBakes(atlas, font, 2);
17273
17274
// Display details
17275
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
17276
SetNextItemWidth(GetFontSize() * 8);
17277
DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
17278
/*SameLine(); MetricsHelpMarker(
17279
"Note that the default embedded font is NOT meant to be scaled.\n\n"
17280
"Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
17281
"You may oversample them to get some flexibility with scaling. "
17282
"You can also render at multiple sizes and select which one to use at runtime.\n\n"
17283
"(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");*/
17284
#endif
17285
17286
char c_str[5];
17287
ImTextCharToUtf8(c_str, font->FallbackChar);
17288
Text("Fallback character: '%s' (U+%04X)", c_str, font->FallbackChar);
17289
ImTextCharToUtf8(c_str, font->EllipsisChar);
17290
Text("Ellipsis character: '%s' (U+%04X)", c_str, font->EllipsisChar);
17291
17292
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17293
{
17294
ImFontConfig* src = font->Sources[src_n];
17295
if (TreeNode(src, "Input %d: \'%s\' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)",
17296
src_n, src->Name, src->FontNo, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y))
17297
{
17298
const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
17299
Text("Loader: '%s'", loader->Name ? loader->Name : "N/A");
17300
17301
//if (DragFloat("ExtraSizeScale", &src->ExtraSizeScale, 0.01f, 0.10f, 2.0f))
17302
// ImFontAtlasFontRebuildOutput(atlas, font);
17303
#ifdef IMGUI_ENABLE_FREETYPE
17304
if (loader->Name != NULL && strcmp(loader->Name, "FreeType") == 0)
17305
{
17306
unsigned int loader_flags = src->FontLoaderFlags;
17307
Text("FreeType Loader Flags: 0x%08X", loader_flags);
17308
if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))
17309
{
17310
ImFontAtlasFontDestroyOutput(atlas, font);
17311
src->FontLoaderFlags = loader_flags;
17312
ImFontAtlasFontInitOutput(atlas, font);
17313
}
17314
}
17315
#endif
17316
TreePop();
17317
}
17318
}
17319
if (font->Sources.Size > 1 && TreeNode("Input Glyphs Overlap Detection Tool"))
17320
{
17321
TextWrapped("- First Input that contains the glyph is used.\n"
17322
"- Use ImFontConfig::GlyphExcludeRanges[] to specify ranges to ignore glyph in given Input.\n- Prefer using a small number of ranges as the list is scanned every time a new glyph is loaded,\n - e.g. GlyphExcludeRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };\n- This tool doesn't cache results and is slow, don't keep it open!");
17323
if (BeginTable("table", 2))
17324
{
17325
for (unsigned int c = 0; c < 0x10000; c++)
17326
if (int overlap_mask = CalcFontGlyphSrcOverlapMask(atlas, font, c))
17327
{
17328
unsigned int c_end = c + 1;
17329
while (c_end < 0x10000 && CalcFontGlyphSrcOverlapMask(atlas, font, c_end) == overlap_mask)
17330
c_end++;
17331
if (TableNextColumn() && TreeNode((void*)(intptr_t)c, "U+%04X-U+%04X: %d codepoints in %d inputs", c, c_end - 1, c_end - c, ImCountSetBits(overlap_mask)))
17332
{
17333
char utf8_buf[5];
17334
for (unsigned int n = c; n < c_end; n++)
17335
{
17336
ImTextCharToUtf8(utf8_buf, n);
17337
BulletText("Codepoint U+%04X (%s)", n, utf8_buf);
17338
}
17339
TreePop();
17340
}
17341
TableNextColumn();
17342
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17343
if (overlap_mask & (1 << src_n))
17344
{
17345
Text("%d ", src_n);
17346
SameLine();
17347
}
17348
c = c_end - 1;
17349
}
17350
EndTable();
17351
}
17352
TreePop();
17353
}
17354
17355
// Display all glyphs of the fonts in separate pages of 256 characters
17356
for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++)
17357
{
17358
ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n];
17359
if (baked->OwnerFont != font)
17360
continue;
17361
PushID(baked_n);
17362
if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : ""))
17363
{
17364
if (SmallButton("Load all"))
17365
for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)
17366
baked->FindGlyph((ImWchar)base);
17367
17368
const int surface_sqrt = (int)ImSqrt((float)baked->MetricsTotalSurface);
17369
Text("Ascent: %f, Descent: %f, Ascent-Descent: %f", baked->Ascent, baked->Descent, baked->Ascent - baked->Descent);
17370
Text("Texture Area: about %d px ~%dx%d px", baked->MetricsTotalSurface, surface_sqrt, surface_sqrt);
17371
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17372
{
17373
ImFontConfig* src = font->Sources[src_n];
17374
int oversample_h, oversample_v;
17375
ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);
17376
BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
17377
src_n, src->Name, src->OversampleH, oversample_h, src->OversampleV, oversample_v, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y);
17378
}
17379
17380
DebugNodeFontGlyphesForSrcMask(font, baked, ~0);
17381
TreePop();
17382
}
17383
PopID();
17384
}
17385
TreePop();
17386
Unindent();
17387
}
17388
17389
void ImGui::DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask)
17390
{
17391
ImDrawList* draw_list = GetWindowDrawList();
17392
const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
17393
const float cell_size = baked->Size * 1;
17394
const float cell_spacing = GetStyle().ItemSpacing.y;
17395
for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
17396
{
17397
// Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
17398
// This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
17399
// is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
17400
if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191))
17401
{
17402
base += 8192 - 256;
17403
continue;
17404
}
17405
17406
int count = 0;
17407
for (unsigned int n = 0; n < 256; n++)
17408
if (const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL)
17409
if (src_mask & (1 << glyph->SourceIdx))
17410
count++;
17411
if (count <= 0)
17412
continue;
17413
if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
17414
continue;
17415
17416
// Draw a 16x16 grid of glyphs
17417
ImVec2 base_pos = GetCursorScreenPos();
17418
for (unsigned int n = 0; n < 256; n++)
17419
{
17420
// We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
17421
// available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
17422
ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
17423
ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
17424
const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL;
17425
draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
17426
if (!glyph || (src_mask & (1 << glyph->SourceIdx)) == 0)
17427
continue;
17428
font->RenderChar(draw_list, cell_size, 0.0f, cell_p1, glyph_col, (ImWchar)(base + n));
17429
if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
17430
{
17431
DebugNodeFontGlyph(font, glyph);
17432
EndTooltip();
17433
}
17434
}
17435
Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
17436
TreePop();
17437
}
17438
}
17439
17440
void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph)
17441
{
17442
Text("Codepoint: U+%04X", glyph->Codepoint);
17443
Separator();
17444
Text("Visible: %d", glyph->Visible);
17445
Text("AdvanceX: %.1f", glyph->AdvanceX);
17446
Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
17447
Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
17448
if (glyph->PackId >= 0)
17449
{
17450
ImTextureRect* r = ImFontAtlasPackGetRect(font->OwnerAtlas, glyph->PackId);
17451
Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y);
17452
}
17453
Text("SourceIdx: %d", glyph->SourceIdx);
17454
}
17455
17456
// [DEBUG] Display contents of ImGuiStorage
17457
void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
17458
{
17459
if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
17460
return;
17461
for (const ImGuiStoragePair& p : storage->Data)
17462
{
17463
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.
17464
DebugLocateItemOnHover(p.key);
17465
}
17466
TreePop();
17467
}
17468
17469
// [DEBUG] Display contents of ImGuiTabBar
17470
void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
17471
{
17472
// Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
17473
char buf[256];
17474
char* p = buf;
17475
const char* buf_end = buf + IM_COUNTOF(buf);
17476
const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
17477
p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
17478
for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
17479
{
17480
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17481
p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
17482
}
17483
p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
17484
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17485
bool open = TreeNode(label, "%s", buf);
17486
if (!is_active) { PopStyleColor(); }
17487
if (is_active && IsItemHovered())
17488
{
17489
ImDrawList* draw_list = GetForegroundDrawList(tab_bar->Window);
17490
draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
17491
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));
17492
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));
17493
}
17494
if (open)
17495
{
17496
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
17497
{
17498
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17499
PushID(tab);
17500
if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
17501
if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
17502
Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
17503
tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
17504
PopID();
17505
}
17506
TreePop();
17507
}
17508
}
17509
17510
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
17511
{
17512
ImGuiContext& g = *GImGui;
17513
SetNextItemOpen(true, ImGuiCond_Once);
17514
bool open = TreeNode("viewport0", "Viewport #%d", 0);
17515
if (IsItemHovered())
17516
g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
17517
if (open)
17518
{
17519
ImGuiWindowFlags flags = viewport->Flags;
17520
BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
17521
viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
17522
viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y);
17523
BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
17524
(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
17525
(flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
17526
(flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
17527
for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
17528
DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
17529
TreePop();
17530
}
17531
}
17532
17533
void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
17534
{
17535
if (window == NULL)
17536
{
17537
BulletText("%s: NULL", label);
17538
return;
17539
}
17540
17541
ImGuiContext& g = *GImGui;
17542
const bool is_active = window->WasActive;
17543
ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
17544
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17545
const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
17546
if (!is_active) { PopStyleColor(); }
17547
if (IsItemHovered() && is_active)
17548
GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
17549
if (!open)
17550
return;
17551
17552
if (window->MemoryCompacted)
17553
TextDisabled("Note: some memory buffers have been compacted/freed.");
17554
17555
if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
17556
g.DebugBreakInWindow = window->ID;
17557
17558
ImGuiWindowFlags flags = window->Flags;
17559
DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
17560
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);
17561
BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
17562
(flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
17563
(flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
17564
(flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
17565
if (flags & ImGuiWindowFlags_ChildWindow)
17566
BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags,
17567
(window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "",
17568
(window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "",
17569
(window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "",
17570
(window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : "");
17571
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" : "");
17572
BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
17573
BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
17574
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17575
{
17576
ImRect r = window->NavRectRel[layer];
17577
if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y)
17578
BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
17579
else
17580
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);
17581
DebugLocateItemOnHover(window->NavLastIds[layer]);
17582
}
17583
const ImVec2* pr = window->NavPreferredScoringPosRel;
17584
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17585
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.
17586
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
17587
if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
17588
if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
17589
if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
17590
if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
17591
if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
17592
{
17593
for (ImGuiOldColumns& columns : window->ColumnsStorage)
17594
DebugNodeColumns(&columns);
17595
TreePop();
17596
}
17597
DebugNodeStorage(&window->StateStorage, "Storage");
17598
TreePop();
17599
}
17600
17601
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
17602
{
17603
if (settings->WantDelete)
17604
BeginDisabled();
17605
Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
17606
settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
17607
if (settings->WantDelete)
17608
EndDisabled();
17609
}
17610
17611
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
17612
{
17613
if (!TreeNode(label, "%s (%d)", label, windows->Size))
17614
return;
17615
for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
17616
{
17617
PushID((*windows)[i]);
17618
DebugNodeWindow((*windows)[i], "Window");
17619
PopID();
17620
}
17621
TreePop();
17622
}
17623
17624
// FIXME-OPT: This is technically suboptimal, but it is simpler this way.
17625
void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
17626
{
17627
for (int i = 0; i < windows_size; i++)
17628
{
17629
ImGuiWindow* window = windows[i];
17630
if (window->ParentWindowInBeginStack != parent_in_begin_stack)
17631
continue;
17632
char buf[20];
17633
ImFormatString(buf, IM_COUNTOF(buf), "[%04d] Window", window->BeginOrderWithinContext);
17634
//BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
17635
DebugNodeWindow(window, buf);
17636
TreePush(buf);
17637
DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
17638
TreePop();
17639
}
17640
}
17641
17642
//-----------------------------------------------------------------------------
17643
// [SECTION] DEBUG LOG WINDOW
17644
//-----------------------------------------------------------------------------
17645
17646
void ImGui::DebugLog(const char* fmt, ...)
17647
{
17648
va_list args;
17649
va_start(args, fmt);
17650
DebugLogV(fmt, args);
17651
va_end(args);
17652
}
17653
17654
void ImGui::DebugLogV(const char* fmt, va_list args)
17655
{
17656
ImGuiContext& g = *GImGui;
17657
const int old_size = g.DebugLogBuf.size();
17658
if (g.ContextName[0] != 0)
17659
g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount);
17660
else
17661
g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
17662
g.DebugLogBuf.appendfv(fmt, args);
17663
g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
17664
17665
const char* str = g.DebugLogBuf.begin() + old_size;
17666
if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
17667
IMGUI_DEBUG_PRINTF("%s", str);
17668
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
17669
if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToDebugger)
17670
{
17671
::OutputDebugStringA("[imgui] ");
17672
::OutputDebugStringA(str);
17673
}
17674
#endif
17675
#ifdef IMGUI_ENABLE_TEST_ENGINE
17676
// IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically
17677
const int new_size = g.DebugLogBuf.size();
17678
const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n');
17679
if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
17680
IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), str);
17681
#endif
17682
}
17683
17684
// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
17685
static void SameLineOrWrap(const ImVec2& size)
17686
{
17687
ImGuiContext& g = *GImGui;
17688
ImGuiWindow* window = g.CurrentWindow;
17689
ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
17690
if (window->WorkRect.Contains(ImRect(pos, pos + size)))
17691
ImGui::SameLine();
17692
}
17693
17694
static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
17695
{
17696
ImGuiContext& g = *GImGui;
17697
ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
17698
SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
17699
17700
bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
17701
if (highlight_errors)
17702
ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
17703
if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
17704
{
17705
g.DebugLogAutoDisableFrames = 2;
17706
g.DebugLogAutoDisableFlags |= flags;
17707
}
17708
if (highlight_errors)
17709
{
17710
ImGui::PopStyleColor();
17711
ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
17712
}
17713
else
17714
{
17715
ImGui::SetItemTooltip("Hold Shift when clicking to enable for 2 frames only (useful for spammy log entries)");
17716
}
17717
}
17718
17719
void ImGui::ShowDebugLogWindow(bool* p_open)
17720
{
17721
ImGuiContext& g = *GImGui;
17722
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)
17723
SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
17724
if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
17725
{
17726
End();
17727
return;
17728
}
17729
17730
ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
17731
CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
17732
SetItemTooltip("(except InputRouting which is spammy)");
17733
17734
ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
17735
ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
17736
ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
17737
ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
17738
ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
17739
ShowDebugLogFlag("Font", ImGuiDebugLogFlags_EventFont);
17740
ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
17741
ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
17742
ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
17743
ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
17744
17745
if (SmallButton("Clear"))
17746
{
17747
g.DebugLogBuf.clear();
17748
g.DebugLogIndex.clear();
17749
g.DebugLogSkippedErrors = 0;
17750
}
17751
SameLine();
17752
if (SmallButton("Copy"))
17753
SetClipboardText(g.DebugLogBuf.c_str());
17754
SameLine();
17755
if (SmallButton("Configure Outputs.."))
17756
OpenPopup("Outputs");
17757
if (BeginPopup("Outputs"))
17758
{
17759
CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);
17760
CheckboxFlags("OutputToDebugger", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToDebugger);
17761
#ifndef IMGUI_ENABLE_TEST_ENGINE
17762
BeginDisabled();
17763
#endif
17764
CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);
17765
#ifndef IMGUI_ENABLE_TEST_ENGINE
17766
EndDisabled();
17767
#endif
17768
EndPopup();
17769
}
17770
17771
BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
17772
17773
const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
17774
g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
17775
17776
ImGuiListClipper clipper;
17777
clipper.Begin(g.DebugLogIndex.size());
17778
while (clipper.Step())
17779
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
17780
DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));
17781
g.DebugLogFlags = backup_log_flags;
17782
if (GetScrollY() >= GetScrollMaxY())
17783
SetScrollHereY(1.0f);
17784
EndChild();
17785
17786
End();
17787
}
17788
17789
// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
17790
void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)
17791
{
17792
TextUnformatted(line_begin, line_end);
17793
if (!IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
17794
return;
17795
ImGuiContext& g = *GImGui;
17796
ImRect text_rect = g.LastItemData.Rect;
17797
for (const char* p = line_begin; p <= line_end - 10; p++)
17798
{
17799
ImGuiID id = 0;
17800
if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10]))
17801
continue;
17802
ImVec2 p0 = CalcTextSize(line_begin, p);
17803
ImVec2 p1 = CalcTextSize(p, p + 10);
17804
g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
17805
if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
17806
DebugLocateItemOnHover(id);
17807
p += 10;
17808
}
17809
}
17810
17811
//-----------------------------------------------------------------------------
17812
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
17813
//-----------------------------------------------------------------------------
17814
17815
// Draw a small cross at current CursorPos in current window's DrawList
17816
void ImGui::DebugDrawCursorPos(ImU32 col)
17817
{
17818
ImGuiContext& g = *GImGui;
17819
ImGuiWindow* window = g.CurrentWindow;
17820
ImVec2 pos = window->DC.CursorPos;
17821
window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
17822
window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
17823
}
17824
17825
// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
17826
void ImGui::DebugDrawLineExtents(ImU32 col)
17827
{
17828
ImGuiContext& g = *GImGui;
17829
ImGuiWindow* window = g.CurrentWindow;
17830
float curr_x = window->DC.CursorPos.x;
17831
float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
17832
float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
17833
window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
17834
window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
17835
window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
17836
}
17837
17838
// Draw last item rect in ForegroundDrawList (so it is always visible)
17839
void ImGui::DebugDrawItemRect(ImU32 col)
17840
{
17841
ImGuiContext& g = *GImGui;
17842
ImGuiWindow* window = g.CurrentWindow;
17843
GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
17844
}
17845
17846
// [DEBUG] Locate item position/rectangle given an ID.
17847
static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green
17848
17849
void ImGui::DebugLocateItem(ImGuiID target_id)
17850
{
17851
ImGuiContext& g = *GImGui;
17852
g.DebugLocateId = target_id;
17853
g.DebugLocateFrames = 2;
17854
g.DebugBreakInLocateId = false;
17855
}
17856
17857
// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
17858
void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
17859
{
17860
if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
17861
return;
17862
ImGuiContext& g = *GImGui;
17863
DebugLocateItem(target_id);
17864
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);
17865
17866
// Can't easily use a context menu here because it will mess with focus, active id etc.
17867
if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
17868
{
17869
DebugBreakButtonTooltip(false, "in ItemAdd()");
17870
if (IsKeyChordPressed(g.DebugBreakKeyChord))
17871
g.DebugBreakInLocateId = true;
17872
}
17873
}
17874
17875
void ImGui::DebugLocateItemResolveWithLastItem()
17876
{
17877
ImGuiContext& g = *GImGui;
17878
17879
// [DEBUG] Debug break requested by user
17880
if (g.DebugBreakInLocateId)
17881
IM_DEBUG_BREAK();
17882
17883
ImGuiLastItemData item_data = g.LastItemData;
17884
g.DebugLocateId = 0;
17885
ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
17886
ImRect r = item_data.Rect;
17887
r.Expand(3.0f);
17888
ImVec2 p1 = g.IO.MousePos;
17889
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);
17890
draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
17891
draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
17892
}
17893
17894
void ImGui::DebugStartItemPicker()
17895
{
17896
ImGuiContext& g = *GImGui;
17897
g.DebugItemPickerActive = true;
17898
}
17899
17900
// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
17901
void ImGui::UpdateDebugToolItemPicker()
17902
{
17903
ImGuiContext& g = *GImGui;
17904
g.DebugItemPickerBreakId = 0;
17905
if (!g.DebugItemPickerActive)
17906
return;
17907
17908
const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
17909
SetMouseCursor(ImGuiMouseCursor_Hand);
17910
if (IsKeyPressed(ImGuiKey_Escape))
17911
g.DebugItemPickerActive = false;
17912
const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
17913
if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
17914
{
17915
g.DebugItemPickerBreakId = hovered_id;
17916
g.DebugItemPickerActive = false;
17917
}
17918
for (int mouse_button = 0; mouse_button < 3; mouse_button++)
17919
if (change_mapping && IsMouseClicked(mouse_button))
17920
g.DebugItemPickerMouseButton = (ImU8)mouse_button;
17921
SetNextWindowBgAlpha(0.70f);
17922
if (!BeginTooltip())
17923
return;
17924
Text("HoveredId: 0x%08X", hovered_id);
17925
Text("Press ESC to abort picking.");
17926
const char* mouse_button_names[] = { "Left", "Right", "Middle" };
17927
if (change_mapping)
17928
Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
17929
else
17930
TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
17931
EndTooltip();
17932
}
17933
17934
// Update queries. The steps are: -1: query Stack, >= 0: query each stack item
17935
// We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
17936
static ImGuiID DebugItemPathQuery_UpdateAndGetHookId(ImGuiDebugItemPathQuery* query, ImGuiID id)
17937
{
17938
// Update query. Clear hook when no active query
17939
if (query->MainID != id)
17940
{
17941
query->MainID = id;
17942
query->Step = -1;
17943
query->Complete = false;
17944
query->Results.resize(0);
17945
query->ResultsDescBuf.resize(0);
17946
}
17947
query->Active = false;
17948
if (id == 0)
17949
return 0;
17950
17951
// Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
17952
if (query->Step >= 0 && query->Step < query->Results.Size)
17953
if (query->Results[query->Step].QuerySuccess || query->Results[query->Step].QueryFrameCount > 2)
17954
query->Step++;
17955
17956
// Update status and hook
17957
query->Complete = (query->Step == query->Results.Size);
17958
if (query->Step == -1)
17959
{
17960
query->Active = true;
17961
return id;
17962
}
17963
else if (query->Step >= 0 && query->Step < query->Results.Size)
17964
{
17965
query->Results[query->Step].QueryFrameCount++;
17966
query->Active = true;
17967
return query->Results[query->Step].ID;
17968
}
17969
return 0;
17970
}
17971
17972
// [DEBUG] ID Stack Tool: update query. Called by NewFrame()
17973
void ImGui::UpdateDebugToolItemPathQuery()
17974
{
17975
ImGuiContext& g = *GImGui;
17976
ImGuiID id = 0;
17977
if (g.DebugIDStackTool.LastActiveFrame + 1 == g.FrameCount)
17978
id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
17979
g.DebugHookIdInfoId = DebugItemPathQuery_UpdateAndGetHookId(&g.DebugItemPathQuery, id);
17980
}
17981
17982
// [DEBUG] ID Stack tool: hooks called by GetID() family functions
17983
void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
17984
{
17985
ImGuiContext& g = *GImGui;
17986
ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery;
17987
if (query->Active == false)
17988
{
17989
IM_ASSERT(id == 0);
17990
return;
17991
}
17992
ImGuiWindow* window = g.CurrentWindow;
17993
17994
// Step -1: stack query
17995
// This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
17996
if (query->Step == -1)
17997
{
17998
IM_ASSERT(query->Results.Size == 0);
17999
query->Step++;
18000
query->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
18001
for (int n = 0; n < window->IDStack.Size + 1; n++)
18002
query->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
18003
return;
18004
}
18005
18006
// Step 0+: query for individual level
18007
IM_ASSERT(query->Step >= 0);
18008
if (query->Step != window->IDStack.Size)
18009
return;
18010
ImGuiStackLevelInfo* info = &query->Results[query->Step];
18011
IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
18012
18013
if (info->DescOffset == -1)
18014
{
18015
const char* result = NULL;
18016
const char* result_end = NULL;
18017
switch (data_type)
18018
{
18019
case ImGuiDataType_S32:
18020
ImFormatStringToTempBuffer(&result, &result_end, "%d", (int)(intptr_t)data_id);
18021
break;
18022
case ImGuiDataType_String:
18023
ImFormatStringToTempBuffer(&result, &result_end, "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id);
18024
break;
18025
case ImGuiDataType_Pointer:
18026
ImFormatStringToTempBuffer(&result, &result_end, "(void*)0x%p", data_id);
18027
break;
18028
case ImGuiDataType_ID:
18029
// PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
18030
ImFormatStringToTempBuffer(&result, &result_end, "0x%08X [override]", id);
18031
break;
18032
default:
18033
IM_ASSERT(0);
18034
}
18035
info->DescOffset = query->ResultsDescBuf.size();
18036
query->ResultsDescBuf.append(result, result_end + 1); // Include zero terminator
18037
}
18038
info->QuerySuccess = true;
18039
if (info->DataType == -1)
18040
info->DataType = (ImS8)data_type;
18041
}
18042
18043
static int DebugItemPathQuery_FormatLevelInfo(ImGuiDebugItemPathQuery* query, int n, bool format_for_ui, char* buf, size_t buf_size)
18044
{
18045
ImGuiStackLevelInfo* info = &query->Results[n];
18046
ImGuiWindow* window = (info->DescOffset == -1 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
18047
if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
18048
return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", ImHashSkipUncontributingPrefix(window->Name));
18049
if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
18050
return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", ImHashSkipUncontributingPrefix(&query->ResultsDescBuf.Buf[info->DescOffset]));
18051
if (query->Step < query->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
18052
return (*buf = 0);
18053
#ifdef IMGUI_ENABLE_TEST_ENGINE
18054
if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
18055
return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", ImHashSkipUncontributingPrefix(label));
18056
#endif
18057
return ImFormatString(buf, buf_size, "???");
18058
}
18059
18060
static const char* DebugItemPathQuery_GetResultAsPath(ImGuiDebugItemPathQuery* query, bool hex_encode_non_ascii_chars)
18061
{
18062
ImGuiTextBuffer* buf = &query->ResultPathBuf;
18063
buf->resize(0);
18064
for (int stack_n = 0; stack_n < query->Results.Size; stack_n++)
18065
{
18066
char level_desc[256];
18067
DebugItemPathQuery_FormatLevelInfo(query, stack_n, false, level_desc, IM_COUNTOF(level_desc));
18068
buf->append(stack_n == 0 ? "//" : "/");
18069
for (const char* p = level_desc; *p != 0; )
18070
{
18071
unsigned int c;
18072
const char* p_next = p + ImTextCharFromUtf8(&c, p, NULL);
18073
if (c == '/')
18074
buf->append("\\");
18075
if (c < 256 || !hex_encode_non_ascii_chars)
18076
buf->append(p, p_next);
18077
else for (; p < p_next; p++)
18078
buf->appendf("\\x%02x", (unsigned char)*p);
18079
p = p_next;
18080
}
18081
}
18082
return buf->c_str();
18083
}
18084
18085
// ID Stack Tool: Display UI
18086
void ImGui::ShowIDStackToolWindow(bool* p_open)
18087
{
18088
ImGuiContext& g = *GImGui;
18089
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)
18090
SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
18091
if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
18092
{
18093
End();
18094
return;
18095
}
18096
18097
ImGuiDebugItemPathQuery* query = &g.DebugItemPathQuery;
18098
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
18099
tool->LastActiveFrame = g.FrameCount;
18100
const char* result_path = DebugItemPathQuery_GetResultAsPath(query, tool->OptHexEncodeNonAsciiChars);
18101
Text("0x%08X", query->MainID);
18102
SameLine();
18103
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.");
18104
18105
// Ctrl+C to copy path
18106
const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
18107
PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f);
18108
Checkbox("Hex-encode non-ASCII", &tool->OptHexEncodeNonAsciiChars);
18109
SameLine();
18110
Checkbox("Ctrl+C: copy path", &tool->OptCopyToClipboardOnCtrlC);
18111
PopStyleVar();
18112
SameLine();
18113
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*");
18114
if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
18115
{
18116
tool->CopyToClipboardLastTime = (float)g.Time;
18117
SetClipboardText(result_path);
18118
}
18119
18120
Text("- Path \"%s\"", query->Complete ? result_path : "");
18121
#ifdef IMGUI_ENABLE_TEST_ENGINE
18122
Text("- Label \"%s\"", query->MainID ? ImGuiTestEngine_FindItemDebugLabel(&g, query->MainID) : "");
18123
#endif
18124
Separator();
18125
18126
// Display decorated stack
18127
if (query->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
18128
{
18129
const float id_width = CalcTextSize("0xDDDDDDDD").x;
18130
TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
18131
TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
18132
TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
18133
TableHeadersRow();
18134
for (int n = 0; n < query->Results.Size; n++)
18135
{
18136
ImGuiStackLevelInfo* info = &query->Results[n];
18137
TableNextColumn();
18138
Text("0x%08X", (n > 0) ? query->Results[n - 1].ID : 0);
18139
TableNextColumn();
18140
DebugItemPathQuery_FormatLevelInfo(query, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
18141
TextUnformatted(g.TempBuffer.Data);
18142
TableNextColumn();
18143
Text("0x%08X", info->ID);
18144
if (n == query->Results.Size - 1)
18145
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
18146
}
18147
EndTable();
18148
}
18149
End();
18150
}
18151
18152
#else
18153
18154
void ImGui::ShowMetricsWindow(bool*) {}
18155
void ImGui::ShowFontAtlas(ImFontAtlas*) {}
18156
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
18157
void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
18158
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
18159
void ImGui::DebugNodeFont(ImFont*) {}
18160
void ImGui::DebugNodeFontGlyphesForSrcMask(ImFont*, ImFontBaked*, int) {}
18161
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
18162
void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
18163
void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
18164
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
18165
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
18166
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
18167
18168
void ImGui::ShowDebugLogWindow(bool*) {}
18169
void ImGui::ShowIDStackToolWindow(bool*) {}
18170
void ImGui::DebugStartItemPicker() {}
18171
void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
18172
18173
#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
18174
18175
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
18176
// Demo helper function to select among loaded fonts.
18177
// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
18178
void ImGui::ShowFontSelector(const char* label)
18179
{
18180
ImGuiIO& io = GetIO();
18181
ImFont* font_current = GetFont();
18182
if (BeginCombo(label, font_current->GetDebugName()))
18183
{
18184
for (ImFont* font : io.Fonts->Fonts)
18185
{
18186
PushID((void*)font);
18187
if (Selectable(font->GetDebugName(), font == font_current, ImGuiSelectableFlags_SelectOnNav))
18188
io.FontDefault = font;
18189
if (font == font_current)
18190
SetItemDefaultFocus();
18191
PopID();
18192
}
18193
EndCombo();
18194
}
18195
SameLine();
18196
if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
18197
MetricsHelpMarker(
18198
"- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
18199
"- Read FAQ and docs/FONTS.md for more details.");
18200
else
18201
MetricsHelpMarker(
18202
"- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
18203
"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
18204
"- Read FAQ and docs/FONTS.md for more details.\n"
18205
"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
18206
}
18207
#endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
18208
18209
//-----------------------------------------------------------------------------
18210
18211
// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
18212
// 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.
18213
#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
18214
#include "imgui_user.inl"
18215
#endif
18216
18217
//-----------------------------------------------------------------------------
18218
18219
#endif // #ifndef IMGUI_DISABLE
18220
18221