Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/imgui/src/imgui.cpp
4246 views
1
// dear imgui, v1.92.0
2
// (main code and documentation)
3
4
// Help:
5
// - See links below.
6
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7
// - Read top of imgui.cpp for more details, links and comments.
8
9
// Resources:
10
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
11
// - Homepage ................... https://github.com/ocornut/imgui
12
// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
13
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
14
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
15
// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
16
// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
17
// - Bindings/Backends https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
18
// - Glossary https://github.com/ocornut/imgui/wiki/Glossary
19
// - Debug Tools https://github.com/ocornut/imgui/wiki/Debug-Tools
20
// - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
21
// - Issues & support ........... https://github.com/ocornut/imgui/issues
22
// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
23
24
// For first-time users having issues compiling/linking/running:
25
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
26
// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
27
// Since 1.92, we encourage font loading question to also be posted in 'Issues'.
28
29
// Copyright (c) 2014-2025 Omar Cornut
30
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
31
// See LICENSE.txt for copyright and licensing details (standard MIT License).
32
// This library is free but needs your support to sustain development and maintenance.
33
// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
34
// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
35
// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
36
37
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
38
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
39
// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
40
// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
41
// to a better solution or official support for them.
42
43
/*
44
45
Index of this file:
46
47
DOCUMENTATION
48
49
- MISSION STATEMENT
50
- CONTROLS GUIDE
51
- PROGRAMMER GUIDE
52
- READ FIRST
53
- HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
54
- GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
55
- HOW A SIMPLE APPLICATION MAY LOOK LIKE
56
- HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
57
- API BREAKING CHANGES (read me when you update!)
58
- FREQUENTLY ASKED QUESTIONS (FAQ)
59
- Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
60
61
CODE
62
(search for "[SECTION]" in the code to find them)
63
64
// [SECTION] INCLUDES
65
// [SECTION] FORWARD DECLARATIONS
66
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
67
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
68
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
69
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
70
// [SECTION] MISC HELPERS/UTILITIES (File functions)
71
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
72
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
73
// [SECTION] ImGuiStorage
74
// [SECTION] ImGuiTextFilter
75
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
76
// [SECTION] ImGuiListClipper
77
// [SECTION] STYLING
78
// [SECTION] RENDER HELPERS
79
// [SECTION] INITIALIZATION, SHUTDOWN
80
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
81
// [SECTION] FONTS
82
// [SECTION] ID STACK
83
// [SECTION] INPUTS
84
// [SECTION] ERROR CHECKING, STATE RECOVERY
85
// [SECTION] ITEM SUBMISSION
86
// [SECTION] LAYOUT
87
// [SECTION] SCROLLING
88
// [SECTION] TOOLTIPS
89
// [SECTION] POPUPS
90
// [SECTION] WINDOW FOCUS
91
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
92
// [SECTION] DRAG AND DROP
93
// [SECTION] LOGGING/CAPTURING
94
// [SECTION] SETTINGS
95
// [SECTION] LOCALIZATION
96
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
97
// [SECTION] PLATFORM DEPENDENT HELPERS
98
// [SECTION] METRICS/DEBUGGER WINDOW
99
// [SECTION] DEBUG LOG WINDOW
100
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
101
102
*/
103
104
//-----------------------------------------------------------------------------
105
// DOCUMENTATION
106
//-----------------------------------------------------------------------------
107
108
/*
109
110
MISSION STATEMENT
111
=================
112
113
- Easy to use to create code-driven and data-driven tools.
114
- Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
115
- Easy to hack and improve.
116
- Minimize setup and maintenance.
117
- Minimize state storage on user side.
118
- Minimize state synchronization.
119
- Portable, minimize dependencies, run on target (consoles, phones, etc.).
120
- Efficient runtime and memory consumption.
121
122
Designed primarily for developers and content-creators, not the typical end-user!
123
Some of the current weaknesses (which we aim to address in the future) includes:
124
125
- Doesn't look fancy.
126
- Limited layout features, intricate layouts are typically crafted in code.
127
128
129
CONTROLS GUIDE
130
==============
131
132
- MOUSE CONTROLS
133
- Mouse wheel: Scroll vertically.
134
- SHIFT+Mouse wheel: Scroll horizontally.
135
- Click [X]: Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
136
- Click ^, Double-Click title: Collapse window.
137
- Drag on corner/border: Resize window (double-click to auto fit window to its contents).
138
- Drag on any empty space: Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
139
- Left-click outside popup: Close popup stack (right-click over underlying popup: Partially close popup stack).
140
141
- TEXT EDITOR
142
- Hold SHIFT or Drag Mouse: Select text.
143
- CTRL+Left/Right: Word jump.
144
- CTRL+Shift+Left/Right: Select words.
145
- CTRL+A or Double-Click: Select All.
146
- CTRL+X, CTRL+C, CTRL+V: Use OS clipboard.
147
- CTRL+Z Undo.
148
- CTRL+Y or CTRL+Shift+Z: Redo.
149
- ESCAPE: Revert text to its original value.
150
- On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors.
151
152
- KEYBOARD CONTROLS
153
- Basic:
154
- Tab, SHIFT+Tab Cycle through text editable fields.
155
- CTRL+Tab, CTRL+Shift+Tab Cycle through windows.
156
- CTRL+Click Input text into a Slider or Drag widget.
157
- Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
158
- Tab, SHIFT+Tab: Cycle through every items.
159
- Arrow keys Move through items using directional navigation. Tweak value.
160
- Arrow keys + Alt, Shift Tweak slower, tweak faster (when using arrow keys).
161
- Enter Activate item (prefer text input when possible).
162
- Space Activate item (prefer tweaking with arrows when possible).
163
- Escape Deactivate item, leave child window, close popup.
164
- Page Up, Page Down Previous page, next page.
165
- Home, End Scroll to top, scroll to bottom.
166
- Alt Toggle between scrolling layer and menu layer.
167
- CTRL+Tab then Ctrl+Arrows Move window. Hold SHIFT to resize instead of moving.
168
- Output when ImGuiConfigFlags_NavEnableKeyboard set,
169
- io.WantCaptureKeyboard flag is set when keyboard is claimed.
170
- io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
171
- io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
172
173
- GAMEPAD CONTROLS
174
- Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
175
- Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
176
- Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
177
- Backend support: backend needs to:
178
- Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
179
- For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
180
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.).
181
- If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
182
with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
183
184
- REMOTE INPUTS SHARING & MOUSE EMULATION
185
- PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
186
- 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)
187
in order to share your PC mouse/keyboard.
188
- See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
189
- 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.
190
Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
191
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.
192
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.
193
(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!)
194
(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
195
to set a boolean to ignore your other external mouse positions until the external source is moved again.)
196
197
198
PROGRAMMER GUIDE
199
================
200
201
READ FIRST
202
----------
203
- Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
204
- Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
205
The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
206
data retention on your side, less state duplication, less state synchronization, fewer bugs.
207
- Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
208
Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
209
- The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
210
- Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
211
You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
212
- Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
213
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,
214
where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
215
- Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
216
- Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
217
If you get an assert, read the messages and comments around the assert.
218
- This codebase aims to be highly optimized:
219
- A typical idle frame should never call malloc/free.
220
- We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
221
- We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
222
Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
223
- This codebase aims to be both highly opinionated and highly flexible:
224
- This code works because of the things it choose to solve or not solve.
225
- C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
226
and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
227
This is to increase compatibility, increase maintainability and facilitate use from other languages.
228
- C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
229
See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
230
We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
231
- C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
232
(so don't use ImVector in your code or at our own risk!).
233
- Building: We don't use nor mandate a build system for the main library.
234
This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
235
This is also because providing a build system for the main library would be of little-value.
236
The build problems are almost never coming from the main library but from specific backends.
237
238
239
HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
240
----------------------------------------------
241
- Update submodule or copy/overwrite every file.
242
- About imconfig.h:
243
- You may modify your copy of imconfig.h, in this case don't overwrite it.
244
- or you may locally branch to modify imconfig.h and merge/rebase latest.
245
- or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
246
specify a custom path for your imconfig.h file and instead not have to modify the default one.
247
248
- Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
249
- Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
250
- You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
251
- Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
252
If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
253
from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
254
likely be a comment about it. Please report any issue to the GitHub page!
255
- To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
256
- Try to keep your copy of Dear ImGui reasonably up to date!
257
258
259
GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
260
---------------------------------------------------------------
261
- See https://github.com/ocornut/imgui/wiki/Getting-Started.
262
- Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
263
- In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
264
- Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
265
It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
266
- 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.
267
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
268
- Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
269
Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
270
phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
271
- Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
272
- 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.
273
274
275
HOW A SIMPLE APPLICATION MAY LOOK LIKE
276
--------------------------------------
277
EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
278
The sub-folders in examples/ contain examples applications following this structure.
279
280
// Application init: create a dear imgui context, setup some options, load fonts
281
ImGui::CreateContext();
282
ImGuiIO& io = ImGui::GetIO();
283
// TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
284
// TODO: Fill optional fields of the io structure later.
285
// TODO: Load TTF/OTF fonts if you don't want to use the default font.
286
287
// Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
288
ImGui_ImplWin32_Init(hwnd);
289
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
290
291
// Application main loop
292
while (true)
293
{
294
// Feed inputs to dear imgui, start new frame
295
ImGui_ImplDX11_NewFrame();
296
ImGui_ImplWin32_NewFrame();
297
ImGui::NewFrame();
298
299
// Any application code here
300
ImGui::Text("Hello, world!");
301
302
// Render dear imgui into framebuffer
303
ImGui::Render();
304
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
305
g_pSwapChain->Present(1, 0);
306
}
307
308
// Shutdown
309
ImGui_ImplDX11_Shutdown();
310
ImGui_ImplWin32_Shutdown();
311
ImGui::DestroyContext();
312
313
EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
314
315
// Application init: create a Dear ImGui context, setup some options, load fonts
316
ImGui::CreateContext();
317
ImGuiIO& io = ImGui::GetIO();
318
// TODO: set io.ConfigXXX values, e.g.
319
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard controls
320
321
// TODO: Load TTF/OTF fonts if you don't want to use the default font.
322
io.Fonts->AddFontFromFileTTF("NotoSans.ttf", 18.0f);
323
324
// Application main loop
325
while (true)
326
{
327
// Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
328
// (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
329
io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
330
io.DisplaySize.x = 1920.0f; // set the current display width
331
io.DisplaySize.y = 1280.0f; // set the current display height here
332
io.AddMousePosEvent(mouse_x, mouse_y); // update mouse position
333
io.AddMouseButtonEvent(0, mouse_b[0]); // update mouse button states
334
io.AddMouseButtonEvent(1, mouse_b[1]); // update mouse button states
335
336
// Call NewFrame(), after this point you can use ImGui::* functions anytime
337
// (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
338
ImGui::NewFrame();
339
340
// Most of your application code here
341
ImGui::Text("Hello, world!");
342
MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
343
MyGameRender(); // may use any Dear ImGui functions as well!
344
345
// End the dear imgui frame
346
// (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)
347
ImGui::EndFrame(); // this is automatically called by Render(), but available
348
ImGui::Render();
349
350
// Update textures
351
ImDrawData* draw_data = ImGui::GetDrawData();
352
for (ImTextureData* tex : *draw_data->Textures)
353
if (tex->Status != ImTextureStatus_OK)
354
MyImGuiBackend_UpdateTexture(tex);
355
356
// Render dear imgui contents, swap buffers
357
MyImGuiBackend_RenderDrawData(draw_data);
358
SwapBuffers();
359
}
360
361
// Shutdown
362
ImGui::DestroyContext();
363
364
To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
365
you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
366
Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
367
368
HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
369
---------------------------------------------
370
The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
371
372
void MyImGuiBackend_UpdateTexture(ImTextureData* tex)
373
{
374
if (tex->Status == ImTextureStatus_WantCreate)
375
{
376
// <create texture based on tex->Width/Height/Pixels>
377
tex->SetTexID(xxxx); // specify backend-specific ImTextureID identifier
378
tex->SetStatus(ImTextureStatus_OK);
379
tex->BackendUserData = xxxx; // store more backend data
380
}
381
if (tex->Status == ImTextureStatus_WantUpdates)
382
{
383
// <update texture blocks based on tex->UpdateRect>
384
tex->SetStatus(ImTextureStatus_OK);
385
}
386
if (tex->Status == ImTextureStatus_WantDestroy)
387
{
388
// <destroy texture>
389
tex->SetTexID(ImTextureID_Invalid);
390
tex->SetStatus(ImTextureStatus_Destroyed);
391
}
392
}
393
394
void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data)
395
{
396
if (draw_data->Textures != nullptr)
397
for (ImTextureData* tex : *draw_data->Textures)
398
if (tex->Status != ImTextureStatus_OK)
399
MyImGuiBackend_UpdateTexture(tex);
400
401
402
// TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
403
// TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.
404
// TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
405
// TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
406
// TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
407
ImVec2 clip_off = draw_data->DisplayPos;
408
for (int n = 0; n < draw_data->CmdListsCount; n++)
409
{
410
const ImDrawList* cmd_list = draw_data->CmdLists[n];
411
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
412
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
413
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
414
{
415
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
416
if (pcmd->UserCallback)
417
{
418
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
419
MyEngineResetRenderState();
420
else
421
pcmd->UserCallback(cmd_list, pcmd);
422
}
423
else
424
{
425
// Project scissor/clipping rectangles into framebuffer space
426
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
427
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
428
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
429
continue;
430
431
// We are using scissoring to clip some objects. All low-level graphics API should support it.
432
// - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
433
// (some elements visible outside their bounds) but you can fix that once everything else works!
434
// - Clipping coordinates are provided in imgui coordinates space:
435
// - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
436
// - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
437
// - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
438
// always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
439
// - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
440
MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y);
441
442
// The texture for the draw call is specified by pcmd->GetTexID().
443
// The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
444
MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
445
446
// Render 'pcmd->ElemCount/3' indexed triangles.
447
// By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
448
MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset);
449
}
450
}
451
}
452
}
453
454
455
API BREAKING CHANGES
456
====================
457
458
Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
459
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.
460
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.
461
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
462
463
- 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
464
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)
465
- Incorrect way to make a window content size 200x200:
466
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
467
- Correct ways to make a window content size 200x200:
468
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
469
Begin(...) + Dummy(ImVec2(200,200)) + End();
470
- TL;DR; if the assert triggers, you can add a Dummy({0,0}) call to validate extending parent boundaries.
471
- 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.
472
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/
473
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+).
474
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.
475
- Hard to read? Refer to 'docs/Changelog.txt' for a less compact and more complete version of this!
476
- 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):
477
This WILL NOT map correctly to the new system! Because font will rasterize as requested size.
478
- 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.
479
- 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.
480
- Fonts: **IMPORTANT** on Font Sizing: Before 1.92, fonts were of a single size. They can now be dynamically sized.
481
- PushFont() API now has a REQUIRED size parameter.
482
- Before 1.92: PushFont() always used font "default" size specified in AddFont() call. It is equivalent to calling PushFont(font, font->LegacySize).
483
- Since 1.92: PushFont(font, 0.0f) preserve the current font size which is a shared value.
484
- To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site.
485
- Kept inline single parameter function. Will obsolete.
486
- Fonts: **IMPORTANT** on Font Merging:
487
- When searching for a glyph in multiple merged fonts: font inputs are now scanned in orderfor the first font input which the desired glyph. This is technically a different behavior than before!
488
- 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. After the update and when using a new backend, those glyphs may now loaded from Font Source 1!
489
- You can use `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to ignore in given Input:
490
// Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range
491
static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
492
ImFontConfig cfg1;
493
cfg1.GlyphExcludeRanges = exclude_ranges;
494
io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1);
495
// Add Font Source 2, which expects to use the range above
496
ImFontConfig cfg2;
497
cfg2.MergeMode = true;
498
io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2);
499
- 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 unde
500
- Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont().
501
- Fonts: Removed support for PushFont(NULL) which was a shortcut for "default font".
502
- Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'.
503
- 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().
504
- 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).
505
- 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.
506
- 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().
507
- Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327)
508
- 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.
509
- Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors.
510
- Fonts: obsoleted ImFont::Scale which is not useful anymore.
511
- 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:
512
- ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef.
513
- ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]
514
- ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount.
515
- Each ImFont has a number of ImFontBaked instances corresponding to actively used sizes. ImFont::GetFontBaked(size) retrieves the one for a given size.
516
- Fields moved from ImFont to ImFontBaked: IndexAdvanceX[], Glyphs[], Ascent, Descent, FindGlyph(), FindGlyphNoFallback(), GetCharAdvance().
517
- 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.)
518
- Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags.
519
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().
520
- old: io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()
521
- new: io.Fonts.FontLoader = ImGuiFreeType::GetFontLoader()
522
- Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV().
523
- The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h.
524
- old:
525
const ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(custom_rect_id);
526
ImVec2 uv0, uv1;
527
atlas->GetCustomRectUV(r, &uv0, &uv1);
528
ImGui::Image(atlas->TexRef, ImVec2(r->w, r->h), uv0, uv1);
529
- new;
530
ImFontAtlasRect r;
531
atlas->GetCustomRect(custom_rect_id, &r);
532
ImGui::Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);
533
- 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.
534
- 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.
535
- Prefer adding a font source (ImFontConfig) using a custom/procedural loader.
536
- DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture().
537
- Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() for all backends that had them. They should not be necessary any more.
538
- 2025/05/23 (1.92.0) - Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition()
539
- old: const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, ....);
540
- new: const char* ImFont::CalcWordWrapPosition (float size, const char* text, ....);
541
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.
542
- 2025/05/15 (1.92.0) - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete).
543
- 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)
544
- 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead.
545
- 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.
546
- 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)
547
- 2025/02/27 (1.91.9) - Image(): removed 'tint_col' and 'border_col' parameter from Image() function. Added ImageWithBg() replacement. (#8131, #8238)
548
- 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));
549
- new: void Image (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1));
550
- 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));
551
- TL;DR: 'border_col' had misleading side-effect on layout, 'bg_col' was missing, parameter order couldn't be consistent with ImageButton().
552
- new behavior always use ImGuiCol_Border color + style.ImageBorderSize / ImGuiStyleVar_ImageBorderSize.
553
- 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.
554
- kept legacy signature (will obsolete), which mimics the old behavior, but uses Max(1.0f, style.ImageBorderSize) when border_col is specified.
555
- 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().
556
- 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.
557
- 2025/02/06 (1.91.9) - renamed ImFontConfig::GlyphExtraSpacing.x to ImFontConfig::GlyphExtraAdvanceX.
558
- 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior.
559
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.
560
the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls:
561
- 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937)
562
- 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.
563
As a result, old .ini data may be partially lost (docking and tables information particularly).
564
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.
565
- 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)
566
- io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
567
- io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
568
- pre-1.87 backends are not supported:
569
- backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
570
- backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
571
- for more reference:
572
- read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
573
- read https://github.com/ocornut/imgui/issues/4921
574
- 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.
575
- 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?
576
- fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements.
577
- 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).
578
- 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
579
moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
580
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!
581
- 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
582
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.
583
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().
584
in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
585
- May warn: ImGui::Image((void*)MyTextureData, ...);
586
- May warn: ImGui::Image((void*)(intptr_t)MyTextureData, ...);
587
- Won't warn: ImGui::Image((ImTextureID)(intptr_t)MyTextureData), ...);
588
- note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
589
- 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)
590
- 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).
591
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)
592
- 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)
593
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()
594
- 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
595
- 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
596
- io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
597
- io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn + same as above line.
598
- io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660)
599
- io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
600
- io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
601
- access those via GetPlatformIO() instead of GetIO().
602
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.
603
- commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:
604
- 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)
605
- new ImageButton() since 1.89 requires an explicit 'const char* str_id'
606
- old ImageButton() before 1.89 had frame_padding' override argument.
607
- new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().
608
- 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
609
you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
610
- instead of: GetWindowContentRegionMax().x - GetCursorPos().x
611
- you can use: GetContentRegionAvail().x
612
- instead of: GetWindowContentRegionMax().x + GetWindowPos().x
613
- you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
614
- instead of: GetContentRegionMax()
615
- you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
616
- instead of: GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
617
- you can use: GetContentRegionAvail() // when called from left edge of window
618
- 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
619
(internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
620
- 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
621
- 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
622
- commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
623
- ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
624
- 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
625
- old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
626
- new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
627
- 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.
628
- old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
629
- new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
630
- 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
631
- 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
632
- 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
633
- 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
634
- 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.
635
- 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
636
- 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
637
- old: CaptureKeyboardFromApp(bool)
638
- new: SetNextFrameWantCaptureKeyboard(bool)
639
- old: CaptureMouseFromApp(bool)
640
- new: SetNextFrameWantCaptureMouse(bool)
641
- 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).
642
- inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
643
- inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
644
- old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
645
- new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
646
- inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
647
- old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
648
- new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
649
- old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
650
- new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
651
for various reasons those changes makes sense. They are being made because making some of those API public.
652
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.
653
- 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.
654
- it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
655
- 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)
656
- 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
657
- 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
658
- 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).
659
- new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
660
with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
661
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.
662
(Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
663
- 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)
664
- 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)
665
- IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
666
- 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
667
- 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
668
- 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
669
- 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.
670
- 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.
671
- 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
672
- 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).
673
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.
674
- 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.
675
- old: BeginChild("Name", size, true)
676
- new: BeginChild("Name", size, ImGuiChildFlags_Border)
677
- old: BeginChild("Name", size, false)
678
- new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
679
**AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
680
- 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.
681
- old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
682
- new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
683
- 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).
684
- 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)
685
- 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).
686
- old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
687
- new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
688
- old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
689
- new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
690
- 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
691
- GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
692
- ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
693
- 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
694
- 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
695
- 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)
696
- 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).
697
- 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
698
- 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.
699
- 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)
700
- 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.
701
- 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.
702
- 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
703
- ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
704
- ListBoxFooter() -> use EndListBox()
705
- 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().
706
- 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
707
- 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
708
- ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp
709
- ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
710
- ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic()
711
- ImDrawList::PathBezierCurveTo() -> use ImDrawList::PathBezierCubicCurveTo()
712
- 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
713
- 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.
714
- 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
715
Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
716
it has been frequently requested by people to use our own. We had an opt-in define which was
717
previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
718
- OK: #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
719
- Error: #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
720
- 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.
721
- 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
722
- 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.
723
- 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
724
- ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl
725
- ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
726
- ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt
727
- ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
728
the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
729
the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
730
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.
731
- 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
732
this will require uses of legacy backend-dependent indices to be casted, e.g.
733
- with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
734
- with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A')
735
- etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
736
- 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);
737
- 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
738
- 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.
739
- 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.
740
- BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
741
- 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
742
this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
743
- previously this would make the window content size ~200x200:
744
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
745
- instead, please submit an item:
746
Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
747
- alternative:
748
Begin(...) + Dummy(ImVec2(200,200)) + End();
749
- content size is now only extended when submitting an item!
750
- with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
751
- without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
752
- 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
753
- added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
754
- 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));
755
- 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.
756
- had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
757
- 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));
758
- requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
759
- always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
760
- 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
761
- Official backends from 1.87+ -> no issue.
762
- Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
763
- Custom backends not writing to io.NavInputs[] -> no issue.
764
- Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
765
- TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
766
- 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
767
- 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.
768
- 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.
769
- 2022/01/20 (1.87) - inputs: reworded gamepad IO.
770
- Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
771
- 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).
772
- 2022/01/17 (1.87) - inputs: reworked mouse IO.
773
- Backend writing to io.MousePos -> backend should call io.AddMousePosEvent()
774
- Backend writing to io.MouseDown[] -> backend should call io.AddMouseButtonEvent()
775
- Backend writing to io.MouseWheel -> backend should call io.AddMouseWheelEvent()
776
- Backend writing to io.MouseHoveredViewport -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
777
note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
778
read https://github.com/ocornut/imgui/issues/4921 for details.
779
- 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.
780
- IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX)
781
- IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
782
- 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).
783
- 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.*
784
- 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.
785
- 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.
786
- 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
787
- 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'.
788
- 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)
789
- ImGui::SetNextTreeNodeOpen() -> use ImGui::SetNextItemOpen()
790
- ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
791
- ImGui::TreeAdvanceToLabelPos() -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
792
- ImFontAtlas::CustomRect -> use ImFontAtlasCustomRect
793
- ImGuiColorEditFlags_RGB/HSV/HEX -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
794
- 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
795
- 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.
796
- 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.
797
- 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
798
- ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
799
- ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
800
- 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
801
- if you are using official backends from the source tree: you have nothing to do.
802
- if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
803
- 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
804
- ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
805
- ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
806
- ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
807
flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
808
breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
809
- rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
810
- rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
811
- rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
812
- rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
813
this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
814
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.
815
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).
816
- 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
817
- ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
818
- 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.
819
- 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.
820
- 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'.
821
- 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.
822
- 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
823
- removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
824
- renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
825
- 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.
826
- renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
827
- renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
828
- 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
829
- ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
830
- ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
831
- ImGuiInputTextCallback -> use ImGuiTextEditCallback
832
- ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
833
- 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
834
- 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
835
- 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
836
- 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
837
- 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
838
- 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
839
- io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
840
- ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
841
- ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
842
- ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
843
- ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
844
- removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
845
- InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
846
- same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
847
- 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).
848
- 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
849
- 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
850
- 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.
851
- 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.
852
- 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!
853
- 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().
854
replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
855
worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
856
- if you omitted the 'power' parameter (likely!), you are not affected.
857
- 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.
858
- 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.
859
see https://github.com/ocornut/imgui/issues/3361 for all details.
860
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.
861
for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
862
- 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.
863
- 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.
864
- 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
865
- 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
866
- 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
867
- 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
868
- 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.
869
- 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.
870
- 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
871
- ShowTestWindow() -> use ShowDemoWindow()
872
- IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
873
- IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
874
- SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
875
- GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
876
- ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
877
- ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
878
- ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
879
- IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
880
- 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.
881
- 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).
882
- 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.
883
- 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
884
- 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
885
- 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
886
- Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
887
- IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
888
- AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
889
- SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
890
- ImFont::Glyph -> use ImFontGlyph
891
- 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.
892
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.
893
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).
894
If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
895
- 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
896
- 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
897
- 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.
898
- 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
899
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.
900
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.
901
Please reach out if you are affected.
902
- 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
903
- 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
904
- 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.
905
- 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
906
- 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
907
- 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
908
- 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!
909
- 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
910
- 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
911
- 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
912
- 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.
913
- 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
914
- 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
915
- 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
916
- 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.
917
If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
918
- 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
919
- 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.
920
NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
921
Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
922
- 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).
923
- 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
924
- 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
925
- 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.
926
- 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
927
- 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
928
- 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
929
- 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.).
930
old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
931
when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
932
in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
933
- 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
934
- 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
935
- 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.
936
If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
937
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.
938
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.
939
- 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",
940
consistent with other functions. Kept redirection functions (will obsolete).
941
- 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.
942
- 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).
943
- 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
944
- 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.
945
- 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
946
- 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.
947
- 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.
948
- 2018/02/07 (1.60) - reorganized context handling to be more explicit,
949
- YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
950
- removed Shutdown() function, as DestroyContext() serve this purpose.
951
- you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
952
- removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
953
- removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
954
- 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.
955
- 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
956
- 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
957
- 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
958
- 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.
959
- 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
960
- 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
961
- 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.
962
- 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.
963
- 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
964
- 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
965
- obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
966
- 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
967
- 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
968
- 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.
969
- 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.
970
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.
971
- 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
972
- 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
973
- 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
974
- 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
975
- 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.
976
- 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.
977
- 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.
978
removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
979
IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
980
IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
981
IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
982
- 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
983
- 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
984
- 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
985
- 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).
986
- 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)".
987
- 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
988
- renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
989
- renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
990
- 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
991
- 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.
992
- 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
993
- 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.
994
- 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).
995
- 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).
996
- 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
997
- 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.
998
- 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.
999
- 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))'
1000
- 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
1001
- 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
1002
- 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
1003
- 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().
1004
- 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
1005
- 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.
1006
- 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.
1007
- 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
1008
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.
1009
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:
1010
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); }
1011
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.
1012
- 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
1013
- 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
1014
- 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).
1015
- 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.
1016
- 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).
1017
- 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)
1018
- 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
1019
- 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.
1020
- 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.
1021
- 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
1022
- 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
1023
- 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
1024
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.
1025
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!
1026
- 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
1027
- 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.
1028
- 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
1029
- 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.
1030
you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
1031
- 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.
1032
this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
1033
- 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.
1034
- the signature of the io.RenderDrawListsFn handler has changed!
1035
old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
1036
new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
1037
parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
1038
ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
1039
ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
1040
- 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.
1041
- 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!
1042
- refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
1043
- 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
1044
- 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
1045
- 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.
1046
- 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
1047
- 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!
1048
- 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
1049
- 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
1050
- 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.
1051
- 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.
1052
- 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
1053
- 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
1054
- 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
1055
- 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
1056
- 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
1057
- 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
1058
- 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
1059
- 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
1060
- 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
1061
- 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
1062
- 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
1063
- 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
1064
- 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
1065
- 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
1066
- 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
1067
- 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
1068
- 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
1069
- 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
1070
- old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
1071
- new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
1072
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.
1073
- 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()
1074
- 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
1075
- 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
1076
- 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
1077
- 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
1078
- 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
1079
- 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
1080
- 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)
1081
- 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
1082
- 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
1083
- 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
1084
- 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
1085
- 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
1086
- 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
1087
1088
1089
FREQUENTLY ASKED QUESTIONS (FAQ)
1090
================================
1091
1092
Read all answers online:
1093
https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
1094
Read all answers locally (with a text editor or ideally a Markdown viewer):
1095
docs/FAQ.md
1096
Some answers are copied down here to facilitate searching in code.
1097
1098
Q&A: Basics
1099
===========
1100
1101
Q: Where is the documentation?
1102
A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
1103
- Run the examples/ applications and explore them.
1104
- Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
1105
- See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
1106
- The demo covers most features of Dear ImGui, so you can read the code and see its output.
1107
- See documentation and comments at the top of imgui.cpp + effectively imgui.h.
1108
- 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
1109
examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
1110
- The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
1111
- The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
1112
- Your programming IDE is your friend, find the type or function declaration to find comments
1113
associated with it.
1114
1115
Q: What is this library called?
1116
Q: Which version should I get?
1117
>> This library is called "Dear ImGui", please don't call it "ImGui" :)
1118
>> See https://www.dearimgui.com/faq for details.
1119
1120
Q&A: Integration
1121
================
1122
1123
Q: How to get started?
1124
A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
1125
1126
Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
1127
A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
1128
>> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
1129
1130
Q. How can I enable keyboard or gamepad controls?
1131
Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
1132
Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
1133
Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
1134
Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
1135
>> See https://www.dearimgui.com/faq
1136
1137
Q&A: Usage
1138
----------
1139
1140
Q: About the ID Stack system..
1141
- Why is my widget not reacting when I click on it?
1142
- How can I have widgets with an empty label?
1143
- How can I have multiple widgets with the same label?
1144
- How can I have multiple windows with the same label?
1145
Q: How can I display an image? What is ImTextureID, how does it work?
1146
Q: How can I use my own math types instead of ImVec2?
1147
Q: How can I interact with standard C++ types (such as std::string and std::vector)?
1148
Q: How can I display custom shapes? (using low-level ImDrawList API)
1149
>> See https://www.dearimgui.com/faq
1150
1151
Q&A: Fonts, Text
1152
================
1153
1154
Q: How should I handle DPI in my application?
1155
Q: How can I load a different font than the default?
1156
Q: How can I easily use icons in my application?
1157
Q: How can I load multiple fonts?
1158
Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
1159
>> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
1160
1161
Q&A: Concerns
1162
=============
1163
1164
Q: Who uses Dear ImGui?
1165
Q: Can you create elaborate/serious tools with Dear ImGui?
1166
Q: Can you reskin the look of Dear ImGui?
1167
Q: Why using C++ (as opposed to C)?
1168
>> See https://www.dearimgui.com/faq
1169
1170
Q&A: Community
1171
==============
1172
1173
Q: How can I help?
1174
A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
1175
We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
1176
This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
1177
>>> See https://github.com/ocornut/imgui/wiki/Funding
1178
- Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1179
- 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!
1180
- Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
1181
You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
1182
But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
1183
- 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).
1184
1185
*/
1186
1187
//-------------------------------------------------------------------------
1188
// [SECTION] INCLUDES
1189
//-------------------------------------------------------------------------
1190
1191
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
1192
#define _CRT_SECURE_NO_WARNINGS
1193
#endif
1194
1195
#ifndef IMGUI_DEFINE_MATH_OPERATORS
1196
#define IMGUI_DEFINE_MATH_OPERATORS
1197
#endif
1198
1199
#include "imgui.h"
1200
#ifndef IMGUI_DISABLE
1201
#include "imgui_internal.h"
1202
1203
// System includes
1204
#include <stdio.h> // vsnprintf, sscanf, printf
1205
#include <stdint.h> // intptr_t
1206
1207
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1208
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1209
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1210
#endif
1211
1212
// [Windows] OS specific includes (optional)
1213
#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)
1214
#define IMGUI_DISABLE_WIN32_FUNCTIONS
1215
#endif
1216
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1217
#ifndef WIN32_LEAN_AND_MEAN
1218
#define WIN32_LEAN_AND_MEAN
1219
#endif
1220
#ifndef NOMINMAX
1221
#define NOMINMAX
1222
#endif
1223
#ifndef __MINGW32__
1224
#include <Windows.h> // _wfopen, OpenClipboard
1225
#else
1226
#include <windows.h>
1227
#endif
1228
#if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
1229
// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1230
#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1231
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1232
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1233
#endif
1234
#endif
1235
1236
// [Apple] OS specific includes
1237
#if defined(__APPLE__)
1238
#include <TargetConditionals.h>
1239
#endif
1240
1241
// Visual Studio warnings
1242
#ifdef _MSC_VER
1243
#pragma warning (disable: 4127) // condition expression is constant
1244
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1245
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
1246
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
1247
#endif
1248
#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).
1249
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1250
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1251
#endif
1252
1253
// Clang/GCC warnings with -Weverything
1254
#if defined(__clang__)
1255
#if __has_warning("-Wunknown-warning-option")
1256
#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!
1257
#endif
1258
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
1259
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
1260
#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.
1261
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
1262
#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.
1263
#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.
1264
#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.
1265
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
1266
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
1267
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
1268
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
1269
#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.
1270
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
1271
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
1272
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
1273
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
1274
#elif defined(__GNUC__)
1275
// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1276
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
1277
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
1278
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
1279
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
1280
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
1281
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
1282
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1283
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
1284
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1285
#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
1286
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
1287
#endif
1288
1289
// Debug options
1290
#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.
1291
#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
1292
1293
// Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls.
1294
static const float FONT_DEFAULT_SIZE = 20.0f;
1295
1296
// 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.
1297
static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
1298
static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
1299
static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut.
1300
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
1301
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.
1302
1303
// Tooltip offset
1304
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
1305
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20); // Multiplied by g.Style.MouseCursorScale
1306
static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f); // Multiplied by g.Style.MouseCursorScale
1307
1308
//-------------------------------------------------------------------------
1309
// [SECTION] FORWARD DECLARATIONS
1310
//-------------------------------------------------------------------------
1311
1312
static void SetCurrentWindow(ImGuiWindow* window);
1313
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1314
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1315
1316
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1317
1318
// Settings
1319
static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1320
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1321
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1322
static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1323
static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1324
1325
// Platform Dependents default implementation for ImGuiPlatformIO functions
1326
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);
1327
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);
1328
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1329
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
1330
1331
namespace ImGui
1332
{
1333
// Item
1334
static void ItemHandleShortcut(ImGuiID id);
1335
1336
// Window Focus
1337
static int FindWindowFocusIndex(ImGuiWindow* window);
1338
static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags);
1339
1340
// Navigation
1341
static void NavUpdate();
1342
static void NavUpdateWindowing();
1343
static void NavUpdateWindowingApplyFocus(ImGuiWindow* window);
1344
static void NavUpdateWindowingOverlay();
1345
static void NavUpdateCancelRequest();
1346
static void NavUpdateCreateMoveRequest();
1347
static void NavUpdateCreateTabbingRequest();
1348
static float NavUpdatePageUpPageDown();
1349
static inline void NavUpdateAnyRequestFlag();
1350
static void NavUpdateCreateWrappingRequest();
1351
static void NavEndFrame();
1352
static bool NavScoreItem(ImGuiNavItemData* result);
1353
static void NavApplyItemToResult(ImGuiNavItemData* result);
1354
static void NavProcessItem();
1355
static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1356
static ImGuiInputSource NavCalcPreferredRefPosSource();
1357
static ImVec2 NavCalcPreferredRefPos();
1358
static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1359
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1360
static void NavRestoreLayer(ImGuiNavLayer layer);
1361
1362
// Error Checking and Debug Tools
1363
static void ErrorCheckNewFrameSanityChecks();
1364
static void ErrorCheckEndFrameSanityChecks();
1365
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1366
static void UpdateDebugToolItemPicker();
1367
static void UpdateDebugToolStackQueries();
1368
static void UpdateDebugToolFlashStyleColor();
1369
#endif
1370
1371
// Inputs
1372
static void UpdateKeyboardInputs();
1373
static void UpdateMouseInputs();
1374
static void UpdateMouseWheel();
1375
static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1376
1377
// Misc
1378
static void UpdateFontsNewFrame();
1379
static void UpdateFontsEndFrame();
1380
static void UpdateTexturesNewFrame();
1381
static void UpdateTexturesEndFrame();
1382
static void UpdateSettings();
1383
static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
1384
static void RenderWindowOuterBorders(ImGuiWindow* window);
1385
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);
1386
static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1387
static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1388
static void RenderDimmedBackgrounds();
1389
static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1390
static void SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect);
1391
1392
// Viewports
1393
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.
1394
static void UpdateViewportsNewFrame();
1395
1396
}
1397
1398
//-----------------------------------------------------------------------------
1399
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1400
//-----------------------------------------------------------------------------
1401
1402
// DLL users:
1403
// - Heaps and globals are not shared across DLL boundaries!
1404
// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1405
// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1406
// - 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.
1407
// - 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).
1408
1409
// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1410
// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1411
// Change to a different context by calling ImGui::SetCurrentContext().
1412
// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1413
// If you want thread-safety to allow N threads to access N different contexts:
1414
// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1415
// struct ImGuiContext;
1416
// extern thread_local ImGuiContext* MyImGuiTLS;
1417
// #define GImGui MyImGuiTLS
1418
// 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.
1419
// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1420
// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1421
// - DLL users: read comments above.
1422
#ifndef GImGui
1423
ImGuiContext* GImGui = NULL;
1424
#endif
1425
1426
// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1427
// - 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.
1428
// - DLL users: read comments above.
1429
#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1430
static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
1431
static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
1432
#else
1433
static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1434
static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1435
#endif
1436
static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
1437
static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
1438
static void* GImAllocatorUserData = NULL;
1439
1440
//-----------------------------------------------------------------------------
1441
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1442
//-----------------------------------------------------------------------------
1443
1444
ImGuiStyle::ImGuiStyle()
1445
{
1446
FontSizeBase = 0.0f; // Will default to io.Fonts->Fonts[0] on first frame.
1447
FontScaleMain = 1.0f; // Main scale factor. May be set by application once, or exposed to end-user.
1448
FontScaleDpi = 1.0f; // Additional scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.
1449
1450
Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
1451
DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1452
WindowPadding = ImVec2(8,8); // Padding within a window
1453
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.
1454
WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1455
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.
1456
WindowMinSize = ImVec2(32,32); // Minimum window size
1457
WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1458
WindowMenuButtonPosition = ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1459
ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1460
ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1461
PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1462
PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1463
FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1464
FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1465
FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1466
ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1467
ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1468
CellPadding = ImVec2(4,2); // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1469
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!
1470
IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1471
ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1472
ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1473
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1474
GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar
1475
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1476
LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1477
ImageBorderSize = 0.0f; // Thickness of border around tabs.
1478
TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1479
TabBorderSize = 0.0f; // Thickness of border around tabs.
1480
TabCloseButtonMinWidthSelected = -1.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
1481
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.
1482
TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1483
TabBarOverlineSize = 1.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar.
1484
TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1485
TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1486
TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone;
1487
TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
1488
TreeLinesRounding = 0.0f; // Radius of lines connecting child nodes to the vertical line.
1489
ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1490
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1491
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.
1492
SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText()
1493
SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1494
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.
1495
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.
1496
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.
1497
MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1498
AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1499
AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1500
AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1501
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.
1502
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.
1503
1504
// Behaviors
1505
HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1506
HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1507
HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1508
HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1509
HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1510
ScrollStepSize = ImVec2(0.0f, 0.0f);// Disabled by default.
1511
ScrollSmooth = 1.0f; // Disabled by default. It's just immediate jump from ScrollExpected to the visual Scroll.
1512
1513
// [Internal]
1514
_MainScale = 1.0f;
1515
_NextFrameFontSizeBase = 0.0f;
1516
1517
// Default theme
1518
ImGui::StyleColorsDark(this);
1519
}
1520
1521
1522
// Scale all spacing/padding/thickness values. Do not scale fonts.
1523
// 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.
1524
void ImGuiStyle::ScaleAllSizes(float scale_factor)
1525
{
1526
_MainScale *= scale_factor;
1527
WindowPadding = ImVec2(ImCeil(WindowPadding.x * scale_factor), ImCeil(WindowPadding.x * scale_factor));
1528
WindowRounding = ImCeil(WindowRounding * scale_factor);
1529
WindowMinSize = ImVec2(ImCeil(WindowMinSize.x * scale_factor), ImCeil(WindowMinSize.y * scale_factor));
1530
WindowBorderHoverPadding = ImCeil(WindowBorderHoverPadding * scale_factor);
1531
ChildRounding = ImCeil(ChildRounding * scale_factor);
1532
PopupRounding = ImCeil(PopupRounding * scale_factor);
1533
FramePadding = ImVec2(ImCeil(FramePadding.x * scale_factor), ImCeil(FramePadding.y * scale_factor));
1534
FrameRounding = ImCeil(FrameRounding * scale_factor);
1535
ItemSpacing = ImVec2(ImCeil(ItemSpacing.x * scale_factor), ImCeil(ItemSpacing.y * scale_factor));
1536
ItemInnerSpacing = ImVec2(ImCeil(ItemInnerSpacing.x * scale_factor), ImCeil(ItemInnerSpacing.y * scale_factor));
1537
CellPadding = ImVec2(ImCeil(CellPadding.x * scale_factor), ImCeil(CellPadding.y * scale_factor));
1538
TouchExtraPadding = ImVec2(ImCeil(TouchExtraPadding.x * scale_factor), ImCeil(TouchExtraPadding.y * scale_factor));
1539
IndentSpacing = ImCeil(IndentSpacing * scale_factor);
1540
ColumnsMinSpacing = ImCeil(ColumnsMinSpacing * scale_factor);
1541
ScrollbarSize = ImCeil(ScrollbarSize * scale_factor);
1542
ScrollbarRounding = ImCeil(ScrollbarRounding * scale_factor);
1543
GrabMinSize = ImCeil(GrabMinSize * scale_factor);
1544
GrabRounding = ImCeil(GrabRounding * scale_factor);
1545
LogSliderDeadzone = ImCeil(LogSliderDeadzone * scale_factor);
1546
ImageBorderSize = ImCeil(ImageBorderSize * scale_factor);
1547
TabRounding = ImCeil(TabRounding * scale_factor);
1548
TabCloseButtonMinWidthSelected = (TabCloseButtonMinWidthSelected > 0.0f && TabCloseButtonMinWidthSelected != FLT_MAX) ? ImCeil(TabCloseButtonMinWidthSelected * scale_factor) : TabCloseButtonMinWidthSelected;
1549
TabCloseButtonMinWidthUnselected = (TabCloseButtonMinWidthUnselected > 0.0f && TabCloseButtonMinWidthUnselected != FLT_MAX) ? ImCeil(TabCloseButtonMinWidthUnselected * scale_factor) : TabCloseButtonMinWidthUnselected;
1550
TabBarOverlineSize = ImCeil(TabBarOverlineSize * scale_factor);
1551
TreeLinesRounding = ImCeil(TreeLinesRounding * scale_factor);
1552
SeparatorTextPadding = ImVec2(ImCeil(SeparatorTextPadding.x * scale_factor), ImCeil(SeparatorTextPadding.y * scale_factor));
1553
DisplayWindowPadding = ImVec2(ImCeil(DisplayWindowPadding.x * scale_factor), ImCeil(DisplayWindowPadding.y * scale_factor));
1554
DisplaySafeAreaPadding = ImVec2(ImCeil(DisplaySafeAreaPadding.x * scale_factor), ImCeil(DisplaySafeAreaPadding.y * scale_factor));
1555
MouseCursorScale = ImCeil(MouseCursorScale * scale_factor);
1556
}
1557
1558
ImGuiIO::ImGuiIO()
1559
{
1560
// Most fields are initialized with zero
1561
memset(this, 0, sizeof(*this));
1562
IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1563
1564
// Settings
1565
ConfigFlags = ImGuiConfigFlags_None;
1566
BackendFlags = ImGuiBackendFlags_None;
1567
DisplaySize = ImVec2(-1.0f, -1.0f);
1568
DeltaTime = 1.0f / 60.0f;
1569
IniSavingRate = 5.0f;
1570
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).
1571
LogFilename = "imgui_log.txt";
1572
UserData = NULL;
1573
1574
Fonts = NULL;
1575
FontDefault = NULL;
1576
FontAllowUserScaling = false;
1577
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1578
FontGlobalScale = 1.0f; // Use style.FontScaleMain instead!
1579
#endif
1580
DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1581
1582
// Keyboard/Gamepad Navigation options
1583
ConfigNavSwapGamepadButtons = false;
1584
ConfigNavMoveSetMousePos = false;
1585
ConfigNavCaptureKeyboard = true;
1586
ConfigNavEscapeClearFocusItem = true;
1587
ConfigNavEscapeClearFocusWindow = false;
1588
ConfigNavCursorVisibleAuto = true;
1589
ConfigNavCursorVisibleAlways = false;
1590
1591
// Miscellaneous options
1592
MouseDrawCursor = false;
1593
#ifdef __APPLE__
1594
ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1595
#else
1596
ConfigMacOSXBehaviors = false;
1597
#endif
1598
ConfigInputTrickleEventQueue = true;
1599
ConfigInputTextCursorBlink = true;
1600
ConfigInputTextEnterKeepActive = false;
1601
ConfigDragClickToInputText = false;
1602
ConfigWindowsResizeFromEdges = true;
1603
ConfigWindowsMoveFromTitleBarOnly = false;
1604
ConfigWindowsCopyContentsWithCtrlC = false;
1605
ConfigScrollbarScrollByPage = true;
1606
ConfigMemoryCompactTimer = 60.0f;
1607
ConfigDebugIsDebuggerPresent = false;
1608
ConfigDebugHighlightIdConflicts = true;
1609
ConfigDebugHighlightIdConflictsShowItemPicker = true;
1610
ConfigDebugBeginReturnValueOnce = false;
1611
ConfigDebugBeginReturnValueLoop = false;
1612
1613
ConfigErrorRecovery = true;
1614
ConfigErrorRecoveryEnableAssert = true;
1615
ConfigErrorRecoveryEnableDebugLog = true;
1616
ConfigErrorRecoveryEnableTooltip = true;
1617
1618
// Inputs Behaviors
1619
MouseDoubleClickTime = 0.30f;
1620
MouseDoubleClickMaxDist = 6.0f;
1621
MouseDragThreshold = 6.0f;
1622
KeyRepeatDelay = 0.275f;
1623
KeyRepeatRate = 0.050f;
1624
1625
// Platform Functions
1626
// Note: Initialize() will setup default clipboard/ime handlers.
1627
BackendPlatformName = BackendRendererName = NULL;
1628
BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1629
1630
// Input (NB: we already have memset zero the entire structure!)
1631
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1632
MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1633
MouseSource = ImGuiMouseSource_Mouse;
1634
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1635
for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1636
AppAcceptingEvents = true;
1637
}
1638
1639
// Pass in translated ASCII characters for text input.
1640
// - with glfw you can get those from the callback set in glfwSetCharCallback()
1641
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1642
// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1643
void ImGuiIO::AddInputCharacter(unsigned int c)
1644
{
1645
IM_ASSERT(Ctx != NULL);
1646
ImGuiContext& g = *Ctx;
1647
if (c == 0 || !AppAcceptingEvents)
1648
return;
1649
1650
ImGuiInputEvent e;
1651
e.Type = ImGuiInputEventType_Text;
1652
e.Source = ImGuiInputSource_Keyboard;
1653
e.EventId = g.InputEventsNextEventId++;
1654
e.Text.Char = c;
1655
g.InputEventsQueue.push_back(e);
1656
}
1657
1658
// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1659
// we should save the high surrogate.
1660
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1661
{
1662
if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1663
return;
1664
1665
if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1666
{
1667
if (InputQueueSurrogate != 0)
1668
AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1669
InputQueueSurrogate = c;
1670
return;
1671
}
1672
1673
ImWchar cp = c;
1674
if (InputQueueSurrogate != 0)
1675
{
1676
if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1677
{
1678
AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1679
}
1680
else
1681
{
1682
#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1683
cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1684
#else
1685
cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1686
#endif
1687
}
1688
1689
InputQueueSurrogate = 0;
1690
}
1691
AddInputCharacter((unsigned)cp);
1692
}
1693
1694
void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1695
{
1696
if (!AppAcceptingEvents)
1697
return;
1698
while (*utf8_chars != 0)
1699
{
1700
unsigned int c = 0;
1701
utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1702
AddInputCharacter(c);
1703
}
1704
}
1705
1706
// Clear all incoming events.
1707
void ImGuiIO::ClearEventsQueue()
1708
{
1709
IM_ASSERT(Ctx != NULL);
1710
ImGuiContext& g = *Ctx;
1711
g.InputEventsQueue.clear();
1712
}
1713
1714
// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1715
void ImGuiIO::ClearInputKeys()
1716
{
1717
ImGuiContext& g = *Ctx;
1718
for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
1719
{
1720
if (ImGui::IsMouseKey((ImGuiKey)key))
1721
continue;
1722
ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
1723
key_data->Down = false;
1724
key_data->DownDuration = -1.0f;
1725
key_data->DownDurationPrev = -1.0f;
1726
}
1727
KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1728
KeyMods = ImGuiMod_None;
1729
InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
1730
}
1731
1732
void ImGuiIO::ClearInputMouse()
1733
{
1734
for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
1735
{
1736
ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN];
1737
key_data->Down = false;
1738
key_data->DownDuration = -1.0f;
1739
key_data->DownDurationPrev = -1.0f;
1740
}
1741
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1742
for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
1743
{
1744
MouseDown[n] = false;
1745
MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1746
}
1747
MouseWheel = MouseWheelH = 0.0f;
1748
}
1749
1750
// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
1751
// Current frame character buffer is now also cleared by ClearInputKeys().
1752
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1753
void ImGuiIO::ClearInputCharacters()
1754
{
1755
InputQueueCharacters.resize(0);
1756
}
1757
#endif
1758
1759
static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1760
{
1761
ImGuiContext& g = *ctx;
1762
for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1763
{
1764
ImGuiInputEvent* e = &g.InputEventsQueue[n];
1765
if (e->Type != type)
1766
continue;
1767
if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1768
continue;
1769
if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1770
continue;
1771
return e;
1772
}
1773
return NULL;
1774
}
1775
1776
// Queue a new key down/up event.
1777
// - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1778
// - bool down: Is the key down? use false to signify a key release.
1779
// - float analog_value: 0.0f..1.0f
1780
// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1781
// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1782
void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1783
{
1784
//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); }
1785
IM_ASSERT(Ctx != NULL);
1786
if (key == ImGuiKey_None || !AppAcceptingEvents)
1787
return;
1788
ImGuiContext& g = *Ctx;
1789
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.
1790
IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1791
1792
// MacOS: swap Cmd(Super) and Ctrl
1793
if (g.IO.ConfigMacOSXBehaviors)
1794
{
1795
if (key == ImGuiMod_Super) { key = ImGuiMod_Ctrl; }
1796
else if (key == ImGuiMod_Ctrl) { key = ImGuiMod_Super; }
1797
else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1798
else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1799
else if (key == ImGuiKey_LeftCtrl) { key = ImGuiKey_LeftSuper; }
1800
else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1801
}
1802
1803
// Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1804
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
1805
const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
1806
const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
1807
const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
1808
if (latest_key_down == down && latest_key_analog == analog_value)
1809
return;
1810
1811
// Add event
1812
ImGuiInputEvent e;
1813
e.Type = ImGuiInputEventType_Key;
1814
e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
1815
e.EventId = g.InputEventsNextEventId++;
1816
e.Key.Key = key;
1817
e.Key.Down = down;
1818
e.Key.AnalogValue = analog_value;
1819
g.InputEventsQueue.push_back(e);
1820
}
1821
1822
void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1823
{
1824
if (!AppAcceptingEvents)
1825
return;
1826
AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
1827
}
1828
1829
// [Optional] Call after AddKeyEvent().
1830
// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
1831
// 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.
1832
void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
1833
{
1834
if (key == ImGuiKey_None)
1835
return;
1836
IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
1837
IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
1838
IM_UNUSED(key); // Yet unused
1839
IM_UNUSED(native_keycode); // Yet unused
1840
IM_UNUSED(native_scancode); // Yet unused
1841
IM_UNUSED(native_legacy_index); // Yet unused
1842
}
1843
1844
// 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.
1845
void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1846
{
1847
AppAcceptingEvents = accepting_events;
1848
}
1849
1850
// Queue a mouse move event
1851
void ImGuiIO::AddMousePosEvent(float x, float y)
1852
{
1853
IM_ASSERT(Ctx != NULL);
1854
ImGuiContext& g = *Ctx;
1855
if (!AppAcceptingEvents)
1856
return;
1857
1858
// Apply same flooring as UpdateMouseInputs()
1859
ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
1860
1861
// Filter duplicate
1862
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
1863
const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1864
if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1865
return;
1866
1867
ImGuiInputEvent e;
1868
e.Type = ImGuiInputEventType_MousePos;
1869
e.Source = ImGuiInputSource_Mouse;
1870
e.EventId = g.InputEventsNextEventId++;
1871
e.MousePos.PosX = pos.x;
1872
e.MousePos.PosY = pos.y;
1873
e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1874
g.InputEventsQueue.push_back(e);
1875
}
1876
1877
void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1878
{
1879
IM_ASSERT(Ctx != NULL);
1880
ImGuiContext& g = *Ctx;
1881
IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1882
if (!AppAcceptingEvents)
1883
return;
1884
1885
// On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1886
if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1887
{
1888
// Order of both statements matters: this event will still release mouse button 1
1889
mouse_button = 1;
1890
if (!down)
1891
MouseCtrlLeftAsRightClick = false;
1892
}
1893
1894
// Filter duplicate
1895
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
1896
const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1897
if (latest_button_down == down)
1898
return;
1899
1900
// On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1901
// - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1902
// - At this point we want from !down to down, so this is handling the initial press.
1903
if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1904
{
1905
const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
1906
if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1907
{
1908
IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1909
MouseCtrlLeftAsRightClick = true;
1910
AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1911
return;
1912
}
1913
}
1914
1915
ImGuiInputEvent e;
1916
e.Type = ImGuiInputEventType_MouseButton;
1917
e.Source = ImGuiInputSource_Mouse;
1918
e.EventId = g.InputEventsNextEventId++;
1919
e.MouseButton.Button = mouse_button;
1920
e.MouseButton.Down = down;
1921
e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1922
g.InputEventsQueue.push_back(e);
1923
}
1924
1925
// Queue a mouse wheel event (some mouse/API may only have a Y component)
1926
void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1927
{
1928
IM_ASSERT(Ctx != NULL);
1929
ImGuiContext& g = *Ctx;
1930
1931
// Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1932
if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1933
return;
1934
1935
ImGuiInputEvent e;
1936
e.Type = ImGuiInputEventType_MouseWheel;
1937
e.Source = ImGuiInputSource_Mouse;
1938
e.EventId = g.InputEventsNextEventId++;
1939
e.MouseWheel.WheelX = wheel_x;
1940
e.MouseWheel.WheelY = wheel_y;
1941
e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1942
g.InputEventsQueue.push_back(e);
1943
}
1944
1945
// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1946
// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1947
void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1948
{
1949
IM_ASSERT(Ctx != NULL);
1950
ImGuiContext& g = *Ctx;
1951
g.InputEventsNextMouseSource = source;
1952
}
1953
1954
void ImGuiIO::AddFocusEvent(bool focused)
1955
{
1956
IM_ASSERT(Ctx != NULL);
1957
ImGuiContext& g = *Ctx;
1958
1959
// Filter duplicate
1960
const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
1961
const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1962
if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1963
return;
1964
1965
ImGuiInputEvent e;
1966
e.Type = ImGuiInputEventType_Focus;
1967
e.EventId = g.InputEventsNextEventId++;
1968
e.AppFocused.Focused = focused;
1969
g.InputEventsQueue.push_back(e);
1970
}
1971
1972
ImGuiPlatformIO::ImGuiPlatformIO()
1973
{
1974
// Most fields are initialized with zero
1975
memset(this, 0, sizeof(*this));
1976
Platform_LocaleDecimalPoint = '.';
1977
}
1978
1979
//-----------------------------------------------------------------------------
1980
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1981
//-----------------------------------------------------------------------------
1982
1983
ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1984
{
1985
IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1986
ImVec2 p_last = p1;
1987
ImVec2 p_closest;
1988
float p_closest_dist2 = FLT_MAX;
1989
float t_step = 1.0f / (float)num_segments;
1990
for (int i_step = 1; i_step <= num_segments; i_step++)
1991
{
1992
ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1993
ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1994
float dist2 = ImLengthSqr(p - p_line);
1995
if (dist2 < p_closest_dist2)
1996
{
1997
p_closest = p_line;
1998
p_closest_dist2 = dist2;
1999
}
2000
p_last = p_current;
2001
}
2002
return p_closest;
2003
}
2004
2005
// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
2006
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)
2007
{
2008
float dx = x4 - x1;
2009
float dy = y4 - y1;
2010
float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
2011
float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
2012
d2 = (d2 >= 0) ? d2 : -d2;
2013
d3 = (d3 >= 0) ? d3 : -d3;
2014
if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
2015
{
2016
ImVec2 p_current(x4, y4);
2017
ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
2018
float dist2 = ImLengthSqr(p - p_line);
2019
if (dist2 < p_closest_dist2)
2020
{
2021
p_closest = p_line;
2022
p_closest_dist2 = dist2;
2023
}
2024
p_last = p_current;
2025
}
2026
else if (level < 10)
2027
{
2028
float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
2029
float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
2030
float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
2031
float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
2032
float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
2033
float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
2034
ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
2035
ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
2036
}
2037
}
2038
2039
// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
2040
// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
2041
ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
2042
{
2043
IM_ASSERT(tess_tol > 0.0f);
2044
ImVec2 p_last = p1;
2045
ImVec2 p_closest;
2046
float p_closest_dist2 = FLT_MAX;
2047
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);
2048
return p_closest;
2049
}
2050
2051
ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
2052
{
2053
ImVec2 ap = p - a;
2054
ImVec2 ab_dir = b - a;
2055
float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
2056
if (dot < 0.0f)
2057
return a;
2058
float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
2059
if (dot > ab_len_sqr)
2060
return b;
2061
return a + ab_dir * dot / ab_len_sqr;
2062
}
2063
2064
bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
2065
{
2066
bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
2067
bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
2068
bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
2069
return ((b1 == b2) && (b2 == b3));
2070
}
2071
2072
void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
2073
{
2074
ImVec2 v0 = b - a;
2075
ImVec2 v1 = c - a;
2076
ImVec2 v2 = p - a;
2077
const float denom = v0.x * v1.y - v1.x * v0.y;
2078
out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
2079
out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
2080
out_u = 1.0f - out_v - out_w;
2081
}
2082
2083
ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
2084
{
2085
ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
2086
ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
2087
ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
2088
float dist2_ab = ImLengthSqr(p - proj_ab);
2089
float dist2_bc = ImLengthSqr(p - proj_bc);
2090
float dist2_ca = ImLengthSqr(p - proj_ca);
2091
float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
2092
if (m == dist2_ab)
2093
return proj_ab;
2094
if (m == dist2_bc)
2095
return proj_bc;
2096
return proj_ca;
2097
}
2098
2099
//-----------------------------------------------------------------------------
2100
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
2101
//-----------------------------------------------------------------------------
2102
2103
// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
2104
int ImStricmp(const char* str1, const char* str2)
2105
{
2106
int d;
2107
while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
2108
return d;
2109
}
2110
2111
int ImStrnicmp(const char* str1, const char* str2, size_t count)
2112
{
2113
int d = 0;
2114
while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
2115
return d;
2116
}
2117
2118
void ImStrncpy(char* dst, const char* src, size_t count)
2119
{
2120
if (count < 1)
2121
return;
2122
if (count > 1)
2123
strncpy(dst, src, count - 1);
2124
dst[count - 1] = 0;
2125
}
2126
2127
char* ImStrdup(const char* str)
2128
{
2129
size_t len = ImStrlen(str);
2130
void* buf = IM_ALLOC(len + 1);
2131
return (char*)memcpy(buf, (const void*)str, len + 1);
2132
}
2133
2134
void* ImMemdup(const void* src, size_t size)
2135
{
2136
void* dst = IM_ALLOC(size);
2137
return memcpy(dst, src, size);
2138
}
2139
2140
char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
2141
{
2142
size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1;
2143
size_t src_size = ImStrlen(src) + 1;
2144
if (dst_buf_size < src_size)
2145
{
2146
IM_FREE(dst);
2147
dst = (char*)IM_ALLOC(src_size);
2148
if (p_dst_size)
2149
*p_dst_size = src_size;
2150
}
2151
return (char*)memcpy(dst, (const void*)src, src_size);
2152
}
2153
2154
const char* ImStrchrRange(const char* str, const char* str_end, char c)
2155
{
2156
const char* p = (const char*)ImMemchr(str, (int)c, str_end - str);
2157
return p;
2158
}
2159
2160
int ImStrlenW(const ImWchar* str)
2161
{
2162
//return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
2163
int n = 0;
2164
while (*str++) n++;
2165
return n;
2166
}
2167
2168
// Find end-of-line. Return pointer will point to either first \n, either str_end.
2169
const char* ImStreolRange(const char* str, const char* str_end)
2170
{
2171
const char* p = (const char*)ImMemchr(str, '\n', str_end - str);
2172
return p ? p : str_end;
2173
}
2174
2175
const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
2176
{
2177
IM_ASSERT_PARANOID(buf_mid_line >= buf_begin && buf_mid_line <= buf_begin + ImStrlen(buf_begin));
2178
while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
2179
buf_mid_line--;
2180
return buf_mid_line;
2181
}
2182
2183
const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
2184
{
2185
if (!needle_end)
2186
needle_end = needle + ImStrlen(needle);
2187
2188
const char un0 = (char)ImToUpper(*needle);
2189
while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
2190
{
2191
if (ImToUpper(*haystack) == un0)
2192
{
2193
const char* b = needle + 1;
2194
for (const char* a = haystack + 1; b < needle_end; a++, b++)
2195
if (ImToUpper(*a) != ImToUpper(*b))
2196
break;
2197
if (b == needle_end)
2198
return haystack;
2199
}
2200
haystack++;
2201
}
2202
return NULL;
2203
}
2204
2205
// 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.
2206
void ImStrTrimBlanks(char* buf)
2207
{
2208
char* p = buf;
2209
while (p[0] == ' ' || p[0] == '\t') // Leading blanks
2210
p++;
2211
char* p_start = p;
2212
while (*p != 0) // Find end of string
2213
p++;
2214
while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
2215
p--;
2216
if (p_start != buf) // Copy memory if we had leading blanks
2217
memmove(buf, p_start, p - p_start);
2218
buf[p - p_start] = 0; // Zero terminate
2219
}
2220
2221
const char* ImStrSkipBlank(const char* str)
2222
{
2223
while (str[0] == ' ' || str[0] == '\t')
2224
str++;
2225
return str;
2226
}
2227
2228
// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
2229
// 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.
2230
// B) When buf==NULL vsnprintf() will return the output size.
2231
#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2232
2233
// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
2234
// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2235
// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
2236
// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
2237
#ifdef IMGUI_USE_STB_SPRINTF
2238
#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
2239
#define STB_SPRINTF_IMPLEMENTATION
2240
#define STB_SPRINTF_STATIC
2241
#endif
2242
#ifdef IMGUI_STB_SPRINTF_FILENAME
2243
#include IMGUI_STB_SPRINTF_FILENAME
2244
#else
2245
#include "stb_sprintf.h"
2246
#endif
2247
#endif // #ifdef IMGUI_USE_STB_SPRINTF
2248
2249
#if defined(_MSC_VER) && !defined(vsnprintf)
2250
#define vsnprintf _vsnprintf
2251
#endif
2252
2253
int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
2254
{
2255
va_list args;
2256
va_start(args, fmt);
2257
#ifdef IMGUI_USE_STB_SPRINTF
2258
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2259
#else
2260
int w = vsnprintf(buf, buf_size, fmt, args);
2261
#endif
2262
va_end(args);
2263
if (buf == NULL)
2264
return w;
2265
if (w == -1 || w >= (int)buf_size)
2266
w = (int)buf_size - 1;
2267
buf[w] = 0;
2268
return w;
2269
}
2270
2271
int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
2272
{
2273
#ifdef IMGUI_USE_STB_SPRINTF
2274
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2275
#else
2276
int w = vsnprintf(buf, buf_size, fmt, args);
2277
#endif
2278
if (buf == NULL)
2279
return w;
2280
if (w == -1 || w >= (int)buf_size)
2281
w = (int)buf_size - 1;
2282
buf[w] = 0;
2283
return w;
2284
}
2285
#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2286
2287
void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
2288
{
2289
va_list args;
2290
va_start(args, fmt);
2291
ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
2292
va_end(args);
2293
}
2294
2295
// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
2296
// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
2297
// ImGuiTempBufferToken token;
2298
// ImFormatStringToTempBuffer(token, ...);
2299
void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
2300
{
2301
ImGuiContext& g = *GImGui;
2302
if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
2303
{
2304
const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
2305
if (buf == NULL)
2306
buf = "(null)";
2307
*out_buf = buf;
2308
if (out_buf_end) { *out_buf_end = buf + ImStrlen(buf); }
2309
}
2310
else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
2311
{
2312
int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
2313
const char* buf = va_arg(args, const char*);
2314
if (buf == NULL)
2315
{
2316
buf = "(null)";
2317
buf_len = ImMin(buf_len, 6);
2318
}
2319
*out_buf = buf;
2320
*out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
2321
}
2322
else
2323
{
2324
int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
2325
*out_buf = g.TempBuffer.Data;
2326
if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
2327
}
2328
}
2329
2330
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2331
// CRC32 needs a 1KB lookup table (not cache friendly)
2332
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
2333
// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
2334
static const ImU32 GCrc32LookupTable[256] =
2335
{
2336
#ifdef IMGUI_USE_LEGACY_CRC32_ADLER
2337
// Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data.
2338
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
2339
0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
2340
0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
2341
0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
2342
0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
2343
0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
2344
0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
2345
0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
2346
0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
2347
0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
2348
0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
2349
0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
2350
0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
2351
0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
2352
0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
2353
0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
2354
#else
2355
// CRC32c table compatible with SSE 4.2 instructions
2356
0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,
2357
0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,
2358
0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,
2359
0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,
2360
0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,
2361
0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,
2362
0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,
2363
0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,
2364
0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,
2365
0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,
2366
0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,
2367
0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,
2368
0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,
2369
0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,
2370
0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
2371
0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
2372
#endif
2373
};
2374
#endif
2375
2376
// Known size hash
2377
// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
2378
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2379
ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
2380
{
2381
ImU32 crc = ~seed;
2382
const unsigned char* data = (const unsigned char*)data_p;
2383
const unsigned char *data_end = (const unsigned char*)data_p + data_size;
2384
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2385
const ImU32* crc32_lut = GCrc32LookupTable;
2386
while (data < data_end)
2387
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
2388
return ~crc;
2389
#else
2390
while (data + 4 <= data_end)
2391
{
2392
crc = _mm_crc32_u32(crc, *(ImU32*)data);
2393
data += 4;
2394
}
2395
while (data < data_end)
2396
crc = _mm_crc32_u8(crc, *data++);
2397
return ~crc;
2398
#endif
2399
}
2400
2401
// Zero-terminated string hash, with support for ### to reset back to seed value
2402
// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
2403
// Because this syntax is rarely used we are optimizing for the common case.
2404
// - If we reach ### in the string we discard the hash so far and reset to the seed.
2405
// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
2406
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2407
ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
2408
{
2409
seed = ~seed;
2410
ImU32 crc = seed;
2411
const unsigned char* data = (const unsigned char*)data_p;
2412
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2413
const ImU32* crc32_lut = GCrc32LookupTable;
2414
#endif
2415
if (data_size != 0)
2416
{
2417
while (data_size-- != 0)
2418
{
2419
unsigned char c = *data++;
2420
if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
2421
crc = seed;
2422
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2423
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2424
#else
2425
crc = _mm_crc32_u8(crc, c);
2426
#endif
2427
}
2428
}
2429
else
2430
{
2431
while (unsigned char c = *data++)
2432
{
2433
if (c == '#' && data[0] == '#' && data[1] == '#')
2434
crc = seed;
2435
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2436
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2437
#else
2438
crc = _mm_crc32_u8(crc, c);
2439
#endif
2440
}
2441
}
2442
return ~crc;
2443
}
2444
2445
//-----------------------------------------------------------------------------
2446
// [SECTION] MISC HELPERS/UTILITIES (File functions)
2447
//-----------------------------------------------------------------------------
2448
2449
// Default file functions
2450
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2451
2452
ImFileHandle ImFileOpen(const char* filename, const char* mode)
2453
{
2454
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!defined(__CYGWIN__) && !defined(__GNUC__)))
2455
// We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
2456
// Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
2457
const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
2458
const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
2459
2460
// Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
2461
// We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
2462
wchar_t local_temp_stack[FILENAME_MAX];
2463
ImVector<wchar_t> local_temp_heap;
2464
if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
2465
local_temp_heap.resize(filename_wsize + mode_wsize);
2466
wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
2467
wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
2468
::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
2469
::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
2470
return ::_wfopen(filename_wbuf, mode_wbuf);
2471
#else
2472
return fopen(filename, mode);
2473
#endif
2474
}
2475
2476
// 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.
2477
bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
2478
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; }
2479
ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
2480
ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
2481
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2482
2483
// Helper: Load file content into memory
2484
// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
2485
// This can't really be used with "rt" because fseek size won't match read size.
2486
void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
2487
{
2488
IM_ASSERT(filename && mode);
2489
if (out_file_size)
2490
*out_file_size = 0;
2491
2492
ImFileHandle f;
2493
if ((f = ImFileOpen(filename, mode)) == NULL)
2494
return NULL;
2495
2496
size_t file_size = (size_t)ImFileGetSize(f);
2497
if (file_size == (size_t)-1)
2498
{
2499
ImFileClose(f);
2500
return NULL;
2501
}
2502
2503
void* file_data = IM_ALLOC(file_size + padding_bytes);
2504
if (file_data == NULL)
2505
{
2506
ImFileClose(f);
2507
return NULL;
2508
}
2509
if (ImFileRead(file_data, 1, file_size, f) != file_size)
2510
{
2511
ImFileClose(f);
2512
IM_FREE(file_data);
2513
return NULL;
2514
}
2515
if (padding_bytes > 0)
2516
memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
2517
2518
ImFileClose(f);
2519
if (out_file_size)
2520
*out_file_size = file_size;
2521
2522
return file_data;
2523
}
2524
2525
//-----------------------------------------------------------------------------
2526
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
2527
//-----------------------------------------------------------------------------
2528
2529
IM_MSVC_RUNTIME_CHECKS_OFF
2530
2531
// Convert UTF-8 to 32-bit character, process single character input.
2532
// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
2533
// We handle UTF-8 decoding error by skipping forward.
2534
int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
2535
{
2536
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 };
2537
static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
2538
static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
2539
static const int shiftc[] = { 0, 18, 12, 6, 0 };
2540
static const int shifte[] = { 0, 6, 4, 2, 0 };
2541
int len = lengths[*(const unsigned char*)in_text >> 3];
2542
int wanted = len + (len ? 0 : 1);
2543
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
const char* 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 out_buf;
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_text_curr)
2704
{
2705
while (in_text_curr > in_text_start)
2706
{
2707
in_text_curr--;
2708
if ((*in_text_curr & 0xC0) != 0x80)
2709
return in_text_curr;
2710
}
2711
return in_text_start;
2712
}
2713
2714
int ImTextCountLines(const char* in_text, const char* in_text_end)
2715
{
2716
if (in_text_end == NULL)
2717
in_text_end = in_text + ImStrlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
2718
int count = 0;
2719
while (in_text < in_text_end)
2720
{
2721
const char* line_end = (const char*)ImMemchr(in_text, '\n', in_text_end - in_text);
2722
in_text = line_end ? line_end + 1 : in_text_end;
2723
count++;
2724
}
2725
return count;
2726
}
2727
2728
IM_MSVC_RUNTIME_CHECKS_RESTORE
2729
2730
//-----------------------------------------------------------------------------
2731
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
2732
// Note: The Convert functions are early design which are not consistent with other API.
2733
//-----------------------------------------------------------------------------
2734
2735
IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
2736
{
2737
float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
2738
int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
2739
int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
2740
int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
2741
return IM_COL32(r, g, b, 0xFF);
2742
}
2743
2744
ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
2745
{
2746
float s = 1.0f / 255.0f;
2747
return ImVec4(
2748
((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
2749
((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
2750
((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
2751
((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
2752
}
2753
2754
ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
2755
{
2756
ImU32 out;
2757
out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
2758
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
2759
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
2760
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
2761
return out;
2762
}
2763
2764
// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
2765
// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
2766
void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
2767
{
2768
float K = 0.f;
2769
if (g < b)
2770
{
2771
ImSwap(g, b);
2772
K = -1.f;
2773
}
2774
if (r < g)
2775
{
2776
ImSwap(r, g);
2777
K = -2.f / 6.f - K;
2778
}
2779
2780
const float chroma = r - (g < b ? g : b);
2781
out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
2782
out_s = chroma / (r + 1e-20f);
2783
out_v = r;
2784
}
2785
2786
// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
2787
// also http://en.wikipedia.org/wiki/HSL_and_HSV
2788
void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
2789
{
2790
if (s == 0.0f)
2791
{
2792
// gray
2793
out_r = out_g = out_b = v;
2794
return;
2795
}
2796
2797
h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
2798
int i = (int)h;
2799
float f = h - (float)i;
2800
float p = v * (1.0f - s);
2801
float q = v * (1.0f - s * f);
2802
float t = v * (1.0f - s * (1.0f - f));
2803
2804
switch (i)
2805
{
2806
case 0: out_r = v; out_g = t; out_b = p; break;
2807
case 1: out_r = q; out_g = v; out_b = p; break;
2808
case 2: out_r = p; out_g = v; out_b = t; break;
2809
case 3: out_r = p; out_g = q; out_b = v; break;
2810
case 4: out_r = t; out_g = p; out_b = v; break;
2811
case 5: default: out_r = v; out_g = p; out_b = q; break;
2812
}
2813
}
2814
2815
//-----------------------------------------------------------------------------
2816
// [SECTION] ImGuiStorage
2817
// Helper: Key->value storage
2818
//-----------------------------------------------------------------------------
2819
2820
// std::lower_bound but without the bullshit
2821
ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
2822
{
2823
ImGuiStoragePair* in_p = in_begin;
2824
for (size_t count = (size_t)(in_end - in_p); count > 0; )
2825
{
2826
size_t count2 = count >> 1;
2827
ImGuiStoragePair* mid = in_p + count2;
2828
if (mid->key < key)
2829
{
2830
in_p = ++mid;
2831
count -= count2 + 1;
2832
}
2833
else
2834
{
2835
count = count2;
2836
}
2837
}
2838
return in_p;
2839
}
2840
2841
IM_MSVC_RUNTIME_CHECKS_OFF
2842
static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
2843
{
2844
// We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
2845
ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
2846
ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
2847
return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
2848
}
2849
2850
// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2851
void ImGuiStorage::BuildSortByKey()
2852
{
2853
ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);
2854
}
2855
2856
int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
2857
{
2858
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2859
if (it == Data.Data + Data.Size || it->key != key)
2860
return default_val;
2861
return it->val_i;
2862
}
2863
2864
bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
2865
{
2866
return GetInt(key, default_val ? 1 : 0) != 0;
2867
}
2868
2869
float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
2870
{
2871
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2872
if (it == Data.Data + Data.Size || it->key != key)
2873
return default_val;
2874
return it->val_f;
2875
}
2876
2877
void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
2878
{
2879
ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2880
if (it == Data.Data + Data.Size || it->key != key)
2881
return NULL;
2882
return it->val_p;
2883
}
2884
2885
// 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.
2886
int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
2887
{
2888
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2889
if (it == Data.Data + Data.Size || it->key != key)
2890
it = Data.insert(it, ImGuiStoragePair(key, default_val));
2891
return &it->val_i;
2892
}
2893
2894
bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2895
{
2896
return (bool*)GetIntRef(key, default_val ? 1 : 0);
2897
}
2898
2899
float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2900
{
2901
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2902
if (it == Data.Data + Data.Size || it->key != key)
2903
it = Data.insert(it, ImGuiStoragePair(key, default_val));
2904
return &it->val_f;
2905
}
2906
2907
void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2908
{
2909
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2910
if (it == Data.Data + Data.Size || it->key != key)
2911
it = Data.insert(it, ImGuiStoragePair(key, default_val));
2912
return &it->val_p;
2913
}
2914
2915
// 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)
2916
void ImGuiStorage::SetInt(ImGuiID key, int val)
2917
{
2918
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2919
if (it == Data.Data + Data.Size || it->key != key)
2920
Data.insert(it, ImGuiStoragePair(key, val));
2921
else
2922
it->val_i = val;
2923
}
2924
2925
void ImGuiStorage::SetBool(ImGuiID key, bool val)
2926
{
2927
SetInt(key, val ? 1 : 0);
2928
}
2929
2930
void ImGuiStorage::SetFloat(ImGuiID key, float val)
2931
{
2932
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2933
if (it == Data.Data + Data.Size || it->key != key)
2934
Data.insert(it, ImGuiStoragePair(key, val));
2935
else
2936
it->val_f = val;
2937
}
2938
2939
void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2940
{
2941
ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2942
if (it == Data.Data + Data.Size || it->key != key)
2943
Data.insert(it, ImGuiStoragePair(key, val));
2944
else
2945
it->val_p = val;
2946
}
2947
2948
void ImGuiStorage::SetAllInt(int v)
2949
{
2950
for (int i = 0; i < Data.Size; i++)
2951
Data[i].val_i = v;
2952
}
2953
IM_MSVC_RUNTIME_CHECKS_RESTORE
2954
2955
//-----------------------------------------------------------------------------
2956
// [SECTION] ImGuiTextFilter
2957
//-----------------------------------------------------------------------------
2958
2959
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2960
ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
2961
{
2962
InputBuf[0] = 0;
2963
CountGrep = 0;
2964
if (default_filter)
2965
{
2966
ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2967
Build();
2968
}
2969
}
2970
2971
bool ImGuiTextFilter::Draw(const char* label, float width)
2972
{
2973
if (width != 0.0f)
2974
ImGui::SetNextItemWidth(width);
2975
bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2976
if (value_changed)
2977
Build();
2978
return value_changed;
2979
}
2980
2981
void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2982
{
2983
out->resize(0);
2984
const char* wb = b;
2985
const char* we = wb;
2986
while (we < e)
2987
{
2988
if (*we == separator)
2989
{
2990
out->push_back(ImGuiTextRange(wb, we));
2991
wb = we + 1;
2992
}
2993
we++;
2994
}
2995
if (wb != we)
2996
out->push_back(ImGuiTextRange(wb, we));
2997
}
2998
2999
void ImGuiTextFilter::Build()
3000
{
3001
Filters.resize(0);
3002
ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf));
3003
input_range.split(',', &Filters);
3004
3005
CountGrep = 0;
3006
for (ImGuiTextRange& f : Filters)
3007
{
3008
while (f.b < f.e && ImCharIsBlankA(f.b[0]))
3009
f.b++;
3010
while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
3011
f.e--;
3012
if (f.empty())
3013
continue;
3014
if (f.b[0] != '-')
3015
CountGrep += 1;
3016
}
3017
}
3018
3019
bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
3020
{
3021
if (Filters.Size == 0)
3022
return true;
3023
3024
if (text == NULL)
3025
text = text_end = "";
3026
3027
for (const ImGuiTextRange& f : Filters)
3028
{
3029
if (f.b == f.e)
3030
continue;
3031
if (f.b[0] == '-')
3032
{
3033
// Subtract
3034
if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
3035
return false;
3036
}
3037
else
3038
{
3039
// Grep
3040
if (ImStristr(text, text_end, f.b, f.e) != NULL)
3041
return true;
3042
}
3043
}
3044
3045
// Implicit * grep
3046
if (CountGrep == 0)
3047
return true;
3048
3049
return false;
3050
}
3051
3052
//-----------------------------------------------------------------------------
3053
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
3054
//-----------------------------------------------------------------------------
3055
3056
// On some platform vsnprintf() takes va_list by reference and modifies it.
3057
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
3058
#ifndef va_copy
3059
#if defined(__GNUC__) || defined(__clang__)
3060
#define va_copy(dest, src) __builtin_va_copy(dest, src)
3061
#else
3062
#define va_copy(dest, src) (dest = src)
3063
#endif
3064
#endif
3065
3066
char ImGuiTextBuffer::EmptyString[1] = { 0 };
3067
3068
void ImGuiTextBuffer::append(const char* str, const char* str_end)
3069
{
3070
int len = str_end ? (int)(str_end - str) : (int)ImStrlen(str);
3071
3072
// Add zero-terminator the first time
3073
const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
3074
const int needed_sz = write_off + len;
3075
if (write_off + len >= Buf.Capacity)
3076
{
3077
int new_capacity = Buf.Capacity * 2;
3078
Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
3079
}
3080
3081
Buf.resize(needed_sz);
3082
memcpy(&Buf[write_off - 1], str, (size_t)len);
3083
Buf[write_off - 1 + len] = 0;
3084
}
3085
3086
void ImGuiTextBuffer::appendf(const char* fmt, ...)
3087
{
3088
va_list args;
3089
va_start(args, fmt);
3090
appendfv(fmt, args);
3091
va_end(args);
3092
}
3093
3094
// Helper: Text buffer for logging/accumulating text
3095
void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
3096
{
3097
va_list args_copy;
3098
va_copy(args_copy, args);
3099
3100
int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
3101
if (len <= 0)
3102
{
3103
va_end(args_copy);
3104
return;
3105
}
3106
3107
// Add zero-terminator the first time
3108
const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
3109
const int needed_sz = write_off + len;
3110
if (write_off + len >= Buf.Capacity)
3111
{
3112
int new_capacity = Buf.Capacity * 2;
3113
Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
3114
}
3115
3116
Buf.resize(needed_sz);
3117
ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
3118
va_end(args_copy);
3119
}
3120
3121
void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
3122
{
3123
IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
3124
if (old_size == new_size)
3125
return;
3126
if (EndOffset == 0 || base[EndOffset - 1] == '\n')
3127
LineOffsets.push_back(EndOffset);
3128
const char* base_end = base + new_size;
3129
for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\n', base_end - p)) != 0; )
3130
if (++p < base_end) // Don't push a trailing offset on last \n
3131
LineOffsets.push_back((int)(intptr_t)(p - base));
3132
EndOffset = ImMax(EndOffset, new_size);
3133
}
3134
3135
//-----------------------------------------------------------------------------
3136
// [SECTION] ImGuiListClipper
3137
//-----------------------------------------------------------------------------
3138
3139
// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
3140
// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
3141
static bool GetSkipItemForListClipping()
3142
{
3143
ImGuiContext& g = *GImGui;
3144
return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
3145
}
3146
3147
static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
3148
{
3149
if (ranges.Size - offset <= 1)
3150
return;
3151
3152
// Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
3153
for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
3154
for (int i = offset; i < sort_end + offset; ++i)
3155
if (ranges[i].Min > ranges[i + 1].Min)
3156
ImSwap(ranges[i], ranges[i + 1]);
3157
3158
// Now fuse ranges together as much as possible.
3159
for (int i = 1 + offset; i < ranges.Size; i++)
3160
{
3161
IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
3162
if (ranges[i - 1].Max < ranges[i].Min)
3163
continue;
3164
ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
3165
ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
3166
ranges.erase(ranges.Data + i);
3167
i--;
3168
}
3169
}
3170
3171
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
3172
{
3173
// Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
3174
// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
3175
// 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?
3176
ImGuiContext& g = *GImGui;
3177
ImGuiWindow* window = g.CurrentWindow;
3178
float off_y = pos_y - window->DC.CursorPos.y;
3179
window->DC.CursorPos.y = pos_y;
3180
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
3181
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.
3182
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.
3183
if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
3184
columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
3185
if (ImGuiTable* table = g.CurrentTable)
3186
{
3187
if (table->IsInsideRow)
3188
ImGui::TableEndRow(table);
3189
table->RowPosY2 = window->DC.CursorPos.y;
3190
const int row_increase = (int)((off_y / line_height) + 0.5f);
3191
//table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
3192
table->RowBgColorCounter += row_increase;
3193
}
3194
}
3195
3196
ImGuiListClipper::ImGuiListClipper()
3197
{
3198
memset(this, 0, sizeof(*this));
3199
}
3200
3201
ImGuiListClipper::~ImGuiListClipper()
3202
{
3203
End();
3204
}
3205
3206
void ImGuiListClipper::Begin(int items_count, float items_height)
3207
{
3208
if (Ctx == NULL)
3209
Ctx = ImGui::GetCurrentContext();
3210
3211
ImGuiContext& g = *Ctx;
3212
ImGuiWindow* window = g.CurrentWindow;
3213
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
3214
3215
if (ImGuiTable* table = g.CurrentTable)
3216
if (table->IsInsideRow)
3217
ImGui::TableEndRow(table);
3218
3219
StartPosY = window->DC.CursorPos.y;
3220
ItemsHeight = items_height;
3221
ItemsCount = items_count;
3222
DisplayStart = -1;
3223
DisplayEnd = 0;
3224
3225
// Acquire temporary buffer
3226
if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
3227
g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
3228
ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3229
data->Reset(this);
3230
data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
3231
TempData = data;
3232
StartSeekOffsetY = data->LossynessOffset;
3233
}
3234
3235
void ImGuiListClipper::End()
3236
{
3237
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
3238
{
3239
// 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.
3240
ImGuiContext& g = *Ctx;
3241
IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
3242
if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
3243
SeekCursorForItem(ItemsCount);
3244
3245
// Restore temporary buffer and fix back pointers which may be invalidated when nesting
3246
IM_ASSERT(data->ListClipper == this);
3247
data->StepNo = data->Ranges.Size;
3248
if (--g.ClipperTempDataStacked > 0)
3249
{
3250
data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3251
data->ListClipper->TempData = data;
3252
}
3253
TempData = NULL;
3254
}
3255
ItemsCount = -1;
3256
}
3257
3258
void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
3259
{
3260
ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
3261
IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
3262
IM_ASSERT(item_begin <= item_end);
3263
if (item_begin < item_end)
3264
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
3265
}
3266
3267
// This is already called while stepping.
3268
// 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.
3269
void ImGuiListClipper::SeekCursorForItem(int item_n)
3270
{
3271
// - Perform the add and multiply with double to allow seeking through larger ranges.
3272
// - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
3273
// - 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.
3274
float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
3275
ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight);
3276
}
3277
3278
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
3279
{
3280
ImGuiContext& g = *clipper->Ctx;
3281
ImGuiWindow* window = g.CurrentWindow;
3282
ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
3283
IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
3284
3285
ImGuiTable* table = g.CurrentTable;
3286
if (table && table->IsInsideRow)
3287
ImGui::TableEndRow(table);
3288
3289
// No items
3290
if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
3291
return false;
3292
3293
// While we are in frozen row state, keep displaying items one by one, unclipped
3294
// FIXME: Could be stored as a table-agnostic state.
3295
if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
3296
{
3297
clipper->DisplayStart = data->ItemsFrozen;
3298
clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
3299
if (clipper->DisplayStart < clipper->DisplayEnd)
3300
data->ItemsFrozen++;
3301
return true;
3302
}
3303
3304
// Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
3305
bool calc_clipping = false;
3306
if (data->StepNo == 0)
3307
{
3308
clipper->StartPosY = window->DC.CursorPos.y;
3309
if (clipper->ItemsHeight <= 0.0f)
3310
{
3311
// Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
3312
data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
3313
clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
3314
clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
3315
data->StepNo = 1;
3316
return true;
3317
}
3318
calc_clipping = true; // If on the first step with known item height, calculate clipping.
3319
}
3320
3321
// Step 1: Let the clipper infer height from first range
3322
if (clipper->ItemsHeight <= 0.0f)
3323
{
3324
IM_ASSERT(data->StepNo == 1);
3325
if (table)
3326
IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
3327
3328
bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
3329
if (affected_by_floating_point_precision)
3330
{
3331
// Mitigation/hack for very large range: assume last time height constitute line height.
3332
clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
3333
window->DC.CursorPos.y = (float)(clipper->StartPosY + clipper->ItemsHeight);
3334
}
3335
else
3336
{
3337
clipper->ItemsHeight = (float)(window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
3338
}
3339
if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
3340
return false;
3341
IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
3342
calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
3343
}
3344
3345
// Step 0 or 1: Calculate the actual ranges of visible elements.
3346
const int already_submitted = clipper->DisplayEnd;
3347
if (calc_clipping)
3348
{
3349
// Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
3350
clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
3351
3352
if (g.LogEnabled)
3353
{
3354
// If logging is active, do not perform any clipping
3355
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
3356
}
3357
else
3358
{
3359
// Add range selected to be included for navigation
3360
const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
3361
if (is_nav_request)
3362
{
3363
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, 0, 0));
3364
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
3365
}
3366
if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
3367
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
3368
3369
// Add focused/active item
3370
ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
3371
if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
3372
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
3373
3374
// Add visible range
3375
float min_y = window->ClipRect.Min.y;
3376
float max_y = window->ClipRect.Max.y;
3377
3378
// Add box selection range
3379
ImGuiBoxSelectState* bs = &g.BoxSelectState;
3380
if (bs->IsActive && bs->Window == window)
3381
{
3382
// FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
3383
// RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
3384
// As a workaround we currently half ItemSpacing worth on each side.
3385
min_y -= g.Style.ItemSpacing.y;
3386
max_y += g.Style.ItemSpacing.y;
3387
3388
// Box-select on 2D area requires different clipping.
3389
if (bs->UnclipMode)
3390
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
3391
}
3392
3393
const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
3394
const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
3395
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max));
3396
}
3397
3398
// Convert position ranges to item index ranges
3399
// - 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.
3400
// - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
3401
// which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
3402
for (ImGuiListClipperRange& range : data->Ranges)
3403
if (range.PosToIndexConvert)
3404
{
3405
int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
3406
int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
3407
range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
3408
range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
3409
range.PosToIndexConvert = false;
3410
}
3411
ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
3412
}
3413
3414
// Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
3415
while (data->StepNo < data->Ranges.Size)
3416
{
3417
clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
3418
clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
3419
data->StepNo++;
3420
if (clipper->DisplayStart >= clipper->DisplayEnd)
3421
continue;
3422
if (clipper->DisplayStart > already_submitted)
3423
clipper->SeekCursorForItem(clipper->DisplayStart);
3424
return true;
3425
}
3426
3427
// After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
3428
// Advance the cursor to the end of the list and then returns 'false' to end the loop.
3429
if (clipper->ItemsCount < INT_MAX)
3430
clipper->SeekCursorForItem(clipper->ItemsCount);
3431
3432
return false;
3433
}
3434
3435
bool ImGuiListClipper::Step()
3436
{
3437
ImGuiContext& g = *Ctx;
3438
bool need_items_height = (ItemsHeight <= 0.0f);
3439
bool ret = ImGuiListClipper_StepInternal(this);
3440
if (ret && (DisplayStart >= DisplayEnd))
3441
ret = false;
3442
if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
3443
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
3444
if (need_items_height && ItemsHeight > 0.0f)
3445
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
3446
if (ret)
3447
{
3448
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
3449
}
3450
else
3451
{
3452
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
3453
End();
3454
}
3455
return ret;
3456
}
3457
3458
//-----------------------------------------------------------------------------
3459
// [SECTION] STYLING
3460
//-----------------------------------------------------------------------------
3461
3462
ImGuiStyle& ImGui::GetStyle()
3463
{
3464
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3465
return GImGui->Style;
3466
}
3467
3468
ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
3469
{
3470
ImGuiStyle& style = GImGui->Style;
3471
ImVec4 c = style.Colors[idx];
3472
c.w *= style.Alpha * alpha_mul;
3473
return ColorConvertFloat4ToU32(c);
3474
}
3475
3476
ImU32 ImGui::GetColorU32(const ImVec4& col)
3477
{
3478
ImGuiStyle& style = GImGui->Style;
3479
ImVec4 c = col;
3480
c.w *= style.Alpha;
3481
return ColorConvertFloat4ToU32(c);
3482
}
3483
3484
const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
3485
{
3486
ImGuiStyle& style = GImGui->Style;
3487
return style.Colors[idx];
3488
}
3489
3490
ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
3491
{
3492
ImGuiStyle& style = GImGui->Style;
3493
alpha_mul *= style.Alpha;
3494
if (alpha_mul >= 1.0f)
3495
return col;
3496
ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
3497
a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
3498
return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
3499
}
3500
3501
// 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
3502
void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
3503
{
3504
ImGuiContext& g = *GImGui;
3505
ImGuiColorMod backup;
3506
backup.Col = idx;
3507
backup.BackupValue = g.Style.Colors[idx];
3508
g.ColorStack.push_back(backup);
3509
if (g.DebugFlashStyleColorIdx != idx)
3510
g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
3511
}
3512
3513
void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
3514
{
3515
ImGuiContext& g = *GImGui;
3516
ImGuiColorMod backup;
3517
backup.Col = idx;
3518
backup.BackupValue = g.Style.Colors[idx];
3519
g.ColorStack.push_back(backup);
3520
if (g.DebugFlashStyleColorIdx != idx)
3521
g.Style.Colors[idx] = col;
3522
}
3523
3524
void ImGui::PopStyleColor(int count)
3525
{
3526
ImGuiContext& g = *GImGui;
3527
if (g.ColorStack.Size < count)
3528
{
3529
IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
3530
count = g.ColorStack.Size;
3531
}
3532
while (count > 0)
3533
{
3534
ImGuiColorMod& backup = g.ColorStack.back();
3535
g.Style.Colors[backup.Col] = backup.BackupValue;
3536
g.ColorStack.pop_back();
3537
count--;
3538
}
3539
}
3540
3541
static const ImGuiStyleVarInfo GStyleVarsInfo[] =
3542
{
3543
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
3544
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
3545
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
3546
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
3547
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
3548
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
3549
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
3550
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
3551
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
3552
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
3553
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
3554
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
3555
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
3556
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
3557
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
3558
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
3559
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
3560
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
3561
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
3562
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
3563
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
3564
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
3565
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize
3566
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
3567
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize
3568
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize
3569
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize
3570
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
3571
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3572
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize
3573
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding
3574
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
3575
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
3576
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
3577
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
3578
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
3579
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollStepSize) }, // ImGuiStyleVar_ScrollStepSize
3580
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollSmooth) }, // ImGuiStyleVar_ScrollSmooth
3581
};
3582
3583
const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
3584
{
3585
IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
3586
IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarsInfo) == ImGuiStyleVar_COUNT);
3587
return &GStyleVarsInfo[idx];
3588
}
3589
3590
void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
3591
{
3592
ImGuiContext& g = *GImGui;
3593
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3594
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 1)
3595
{
3596
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3597
return;
3598
}
3599
float* pvar = (float*)var_info->GetVarPtr(&g.Style);
3600
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3601
*pvar = val;
3602
}
3603
3604
void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)
3605
{
3606
ImGuiContext& g = *GImGui;
3607
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3608
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3609
{
3610
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3611
return;
3612
}
3613
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3614
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3615
pvar->x = val_x;
3616
}
3617
3618
void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)
3619
{
3620
ImGuiContext& g = *GImGui;
3621
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3622
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3623
{
3624
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3625
return;
3626
}
3627
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3628
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3629
pvar->y = val_y;
3630
}
3631
3632
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
3633
{
3634
ImGuiContext& g = *GImGui;
3635
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3636
if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3637
{
3638
IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3639
return;
3640
}
3641
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3642
g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3643
*pvar = val;
3644
}
3645
3646
void ImGui::PopStyleVar(int count)
3647
{
3648
ImGuiContext& g = *GImGui;
3649
if (g.StyleVarStack.Size < count)
3650
{
3651
IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
3652
count = g.StyleVarStack.Size;
3653
}
3654
while (count > 0)
3655
{
3656
// We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
3657
ImGuiStyleMod& backup = g.StyleVarStack.back();
3658
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(backup.VarIdx);
3659
void* data = var_info->GetVarPtr(&g.Style);
3660
if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
3661
else if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
3662
g.StyleVarStack.pop_back();
3663
count--;
3664
}
3665
}
3666
3667
const char* ImGui::GetStyleColorName(ImGuiCol idx)
3668
{
3669
// Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
3670
switch (idx)
3671
{
3672
case ImGuiCol_Text: return "Text";
3673
case ImGuiCol_TextDisabled: return "TextDisabled";
3674
case ImGuiCol_WindowBg: return "WindowBg";
3675
case ImGuiCol_ChildBg: return "ChildBg";
3676
case ImGuiCol_PopupBg: return "PopupBg";
3677
case ImGuiCol_Border: return "Border";
3678
case ImGuiCol_BorderShadow: return "BorderShadow";
3679
case ImGuiCol_FrameBg: return "FrameBg";
3680
case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
3681
case ImGuiCol_FrameBgActive: return "FrameBgActive";
3682
case ImGuiCol_TitleBg: return "TitleBg";
3683
case ImGuiCol_TitleBgActive: return "TitleBgActive";
3684
case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
3685
case ImGuiCol_MenuBarBg: return "MenuBarBg";
3686
case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
3687
case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
3688
case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
3689
case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
3690
case ImGuiCol_CheckMark: return "CheckMark";
3691
case ImGuiCol_SliderGrab: return "SliderGrab";
3692
case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
3693
case ImGuiCol_Button: return "Button";
3694
case ImGuiCol_ButtonHovered: return "ButtonHovered";
3695
case ImGuiCol_ButtonActive: return "ButtonActive";
3696
case ImGuiCol_Header: return "Header";
3697
case ImGuiCol_HeaderHovered: return "HeaderHovered";
3698
case ImGuiCol_HeaderActive: return "HeaderActive";
3699
case ImGuiCol_Separator: return "Separator";
3700
case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
3701
case ImGuiCol_SeparatorActive: return "SeparatorActive";
3702
case ImGuiCol_ResizeGrip: return "ResizeGrip";
3703
case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
3704
case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
3705
case ImGuiCol_InputTextCursor: return "InputTextCursor";
3706
case ImGuiCol_TabHovered: return "TabHovered";
3707
case ImGuiCol_Tab: return "Tab";
3708
case ImGuiCol_TabSelected: return "TabSelected";
3709
case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
3710
case ImGuiCol_TabDimmed: return "TabDimmed";
3711
case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
3712
case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
3713
case ImGuiCol_PlotLines: return "PlotLines";
3714
case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
3715
case ImGuiCol_PlotHistogram: return "PlotHistogram";
3716
case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
3717
case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
3718
case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
3719
case ImGuiCol_TableBorderLight: return "TableBorderLight";
3720
case ImGuiCol_TableRowBg: return "TableRowBg";
3721
case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
3722
case ImGuiCol_TextLink: return "TextLink";
3723
case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3724
case ImGuiCol_TreeLines: return "TreeLines";
3725
case ImGuiCol_DragDropTarget: return "DragDropTarget";
3726
case ImGuiCol_NavCursor: return "NavCursor";
3727
case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
3728
case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
3729
case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
3730
}
3731
IM_ASSERT(0);
3732
return "Unknown";
3733
}
3734
3735
//-----------------------------------------------------------------------------
3736
// [SECTION] RENDER HELPERS
3737
// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
3738
// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
3739
// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
3740
//-----------------------------------------------------------------------------
3741
3742
const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
3743
{
3744
const char* text_display_end = text;
3745
if (!text_end)
3746
text_end = (const char*)-1;
3747
3748
while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
3749
text_display_end++;
3750
return text_display_end;
3751
}
3752
3753
// Internal ImGui functions to render text
3754
// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3755
void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3756
{
3757
ImGuiContext& g = *GImGui;
3758
ImGuiWindow* window = g.CurrentWindow;
3759
3760
// Hide anything after a '##' string
3761
const char* text_display_end;
3762
if (hide_text_after_hash)
3763
{
3764
text_display_end = FindRenderedTextEnd(text, text_end);
3765
}
3766
else
3767
{
3768
if (!text_end)
3769
text_end = text + ImStrlen(text); // FIXME-OPT
3770
text_display_end = text_end;
3771
}
3772
3773
if (text != text_display_end)
3774
{
3775
window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3776
if (g.LogEnabled)
3777
LogRenderedText(&pos, text, text_display_end);
3778
}
3779
}
3780
3781
void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3782
{
3783
ImGuiContext& g = *GImGui;
3784
ImGuiWindow* window = g.CurrentWindow;
3785
3786
if (!text_end)
3787
text_end = text + ImStrlen(text); // FIXME-OPT
3788
3789
if (text != text_end)
3790
{
3791
window->DrawList->AddText(g.Font, g.FontSize, g.FontWeight, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3792
if (g.LogEnabled)
3793
LogRenderedText(&pos, text, text_end);
3794
}
3795
}
3796
3797
// Default clip_rect uses (pos_min,pos_max)
3798
// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3799
// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.
3800
// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
3801
// better advantage of the render function taking size into account for coarse clipping.
3802
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)
3803
{
3804
// Perform CPU side clipping for single clipped element to avoid using scissor state
3805
ImVec2 pos = pos_min;
3806
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3807
3808
const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3809
const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3810
bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3811
if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3812
need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3813
3814
// Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3815
if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3816
if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3817
3818
// Render
3819
if (need_clipping)
3820
{
3821
ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3822
draw_list->AddText(NULL, 0.0f, 0, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3823
}
3824
else
3825
{
3826
draw_list->AddText(NULL, 0.0f, 0, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3827
}
3828
}
3829
3830
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)
3831
{
3832
// Hide anything after a '##' string
3833
const char* text_display_end = FindRenderedTextEnd(text, text_end);
3834
const int text_len = (int)(text_display_end - text);
3835
if (text_len == 0)
3836
return;
3837
3838
ImGuiContext& g = *GImGui;
3839
ImGuiWindow* window = g.CurrentWindow;
3840
RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
3841
if (g.LogEnabled)
3842
LogRenderedText(&pos_min, text, text_display_end);
3843
}
3844
3845
// Another overly complex function until we reorganize everything into a nice all-in-one helper.
3846
// 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.
3847
// 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.
3848
// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceeding 'float ellipsis_max' and was the same value for 99% of users.
3849
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)
3850
{
3851
ImGuiContext& g = *GImGui;
3852
if (text_end_full == NULL)
3853
text_end_full = FindRenderedTextEnd(text);
3854
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
3855
3856
//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));
3857
//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));
3858
3859
// FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
3860
if (text_size.x > pos_max.x - pos_min.x)
3861
{
3862
// Hello wo...
3863
// | | |
3864
// min max ellipsis_max
3865
// <-> this is generally some padding value
3866
3867
ImFont* font = draw_list->_Data->Font;
3868
const float font_size = draw_list->_Data->FontSize;
3869
const float font_scale = draw_list->_Data->FontScale;
3870
const float font_weight = draw_list->_Data->FontWeight;
3871
const char* text_end_ellipsis = NULL;
3872
ImFontBaked* baked = font->GetFontBaked(font_size, font_weight);
3873
const float ellipsis_width = baked->GetCharAdvance(font->EllipsisChar) * font_scale;
3874
3875
// We can now claim the space between pos_max.x and ellipsis_max.x
3876
const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
3877
float text_size_clipped_x = font->CalcTextSizeA(font_size, font_weight, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
3878
while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
3879
{
3880
// 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)
3881
text_end_ellipsis--;
3882
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
3883
}
3884
3885
// Render text, render ellipsis
3886
RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
3887
ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);
3888
ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
3889
font->RenderChar(draw_list, font_size, font_weight, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);
3890
}
3891
else
3892
{
3893
RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
3894
}
3895
3896
if (g.LogEnabled)
3897
LogRenderedText(&pos_min, text, text_end_full);
3898
}
3899
3900
// Render a rectangle shaped with optional rounding and borders
3901
void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)
3902
{
3903
ImGuiContext& g = *GImGui;
3904
ImGuiWindow* window = g.CurrentWindow;
3905
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3906
const float border_size = g.Style.FrameBorderSize;
3907
if (borders && border_size > 0.0f)
3908
{
3909
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3910
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3911
}
3912
}
3913
3914
void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3915
{
3916
ImGuiContext& g = *GImGui;
3917
ImGuiWindow* window = g.CurrentWindow;
3918
const float border_size = g.Style.FrameBorderSize;
3919
if (border_size > 0.0f)
3920
{
3921
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3922
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3923
}
3924
}
3925
3926
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
3927
{
3928
ImGuiContext& g = *GImGui;
3929
if (id != g.NavId)
3930
return;
3931
if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
3932
return;
3933
if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
3934
return;
3935
ImGuiWindow* window = g.CurrentWindow;
3936
if (window->DC.NavHideHighlightOneFrame)
3937
return;
3938
3939
float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
3940
ImRect display_rect = bb;
3941
display_rect.ClipWith(window->ClipRect);
3942
const float thickness = 2.0f;
3943
if (flags & ImGuiNavRenderCursorFlags_Compact)
3944
{
3945
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3946
}
3947
else
3948
{
3949
const float distance = 3.0f + thickness * 0.5f;
3950
display_rect.Expand(ImVec2(distance, distance));
3951
bool fully_visible = window->ClipRect.Contains(display_rect);
3952
if (!fully_visible)
3953
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
3954
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3955
if (!fully_visible)
3956
window->DrawList->PopClipRect();
3957
}
3958
}
3959
3960
void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
3961
{
3962
ImGuiContext& g = *GImGui;
3963
if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
3964
mouse_cursor = ImGuiMouseCursor_Arrow;
3965
ImFontAtlas* font_atlas = g.DrawListSharedData.FontAtlas;
3966
for (ImGuiViewportP* viewport : g.Viewports)
3967
{
3968
// We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
3969
ImVec2 offset, size, uv[4];
3970
if (!ImFontAtlasGetMouseCursorTexData(font_atlas, mouse_cursor, &offset, &size, &uv[0], &uv[2]))
3971
continue;
3972
const ImVec2 pos = base_pos - offset;
3973
const float scale = base_scale;
3974
if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
3975
continue;
3976
ImDrawList* draw_list = GetForegroundDrawList(viewport);
3977
ImTextureRef tex_ref = font_atlas->TexRef;
3978
draw_list->PushTexture(tex_ref);
3979
draw_list->AddImage(tex_ref, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
3980
draw_list->AddImage(tex_ref, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
3981
draw_list->AddImage(tex_ref, pos, pos + size * scale, uv[2], uv[3], col_border);
3982
draw_list->AddImage(tex_ref, pos, pos + size * scale, uv[0], uv[1], col_fill);
3983
if (mouse_cursor == ImGuiMouseCursor_Wait || mouse_cursor == ImGuiMouseCursor_Progress)
3984
{
3985
float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI);
3986
float a_max = a_min + IM_PI * 1.65f;
3987
draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max);
3988
draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale);
3989
}
3990
draw_list->PopTexture();
3991
}
3992
}
3993
3994
//-----------------------------------------------------------------------------
3995
// [SECTION] INITIALIZATION, SHUTDOWN
3996
//-----------------------------------------------------------------------------
3997
3998
// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3999
// 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
4000
ImGuiContext* ImGui::GetCurrentContext()
4001
{
4002
return GImGui;
4003
}
4004
4005
void ImGui::SetCurrentContext(ImGuiContext* ctx)
4006
{
4007
#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
4008
IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
4009
#else
4010
GImGui = ctx;
4011
#endif
4012
}
4013
4014
void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
4015
{
4016
GImAllocatorAllocFunc = alloc_func;
4017
GImAllocatorFreeFunc = free_func;
4018
GImAllocatorUserData = user_data;
4019
}
4020
4021
// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
4022
void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
4023
{
4024
*p_alloc_func = GImAllocatorAllocFunc;
4025
*p_free_func = GImAllocatorFreeFunc;
4026
*p_user_data = GImAllocatorUserData;
4027
}
4028
4029
ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
4030
{
4031
ImGuiContext* prev_ctx = GetCurrentContext();
4032
ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
4033
SetCurrentContext(ctx);
4034
Initialize();
4035
if (prev_ctx != NULL)
4036
SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
4037
return ctx;
4038
}
4039
4040
void ImGui::DestroyContext(ImGuiContext* ctx)
4041
{
4042
ImGuiContext* prev_ctx = GetCurrentContext();
4043
if (ctx == NULL) //-V1051
4044
ctx = prev_ctx;
4045
SetCurrentContext(ctx);
4046
Shutdown();
4047
SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
4048
IM_DELETE(ctx);
4049
}
4050
4051
// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
4052
static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
4053
{
4054
{ ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
4055
{ ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
4056
{ ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
4057
{ ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
4058
{ ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" },
4059
{ ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" },
4060
{ ImGuiLocKey_WindowingPopup, "(Popup)" },
4061
{ ImGuiLocKey_WindowingUntitled, "(Untitled)" },
4062
{ ImGuiLocKey_OpenLink_s, "Open '%s'" },
4063
{ ImGuiLocKey_CopyLink, "Copy Link###CopyLink" },
4064
};
4065
4066
ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
4067
{
4068
IO.Ctx = this;
4069
InputTextState.Ctx = this;
4070
4071
Initialized = false;
4072
Font = NULL;
4073
FontBaked = NULL;
4074
FontSize = FontSizeBase = FontBakedScale = FontWeight = CurrentDpiScale = 0.0f;
4075
FontRasterizerDensity = 1.0f;
4076
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
4077
if (shared_font_atlas == NULL)
4078
IO.Fonts->OwnerContext = this;
4079
Time = 0.0f;
4080
FrameCount = 0;
4081
FrameCountEnded = FrameCountRendered = -1;
4082
WithinEndChildID = 0;
4083
WithinFrameScope = WithinFrameScopeWithImplicitWindow = false;
4084
GcCompactAll = false;
4085
TestEngineHookItems = false;
4086
TestEngine = NULL;
4087
memset(ContextName, 0, sizeof(ContextName));
4088
4089
InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
4090
InputEventsNextEventId = 1;
4091
4092
WindowsActiveCount = 0;
4093
WindowsBorderHoverPadding = 0.0f;
4094
CurrentWindow = NULL;
4095
HoveredWindow = NULL;
4096
HoveredWindowUnderMovingWindow = NULL;
4097
HoveredWindowBeforeClear = NULL;
4098
MovingWindow = NULL;
4099
WheelingWindow = NULL;
4100
WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
4101
WheelingWindowReleaseTimer = 0.0f;
4102
4103
DebugDrawIdConflicts = 0;
4104
DebugHookIdInfo = 0;
4105
HoveredId = HoveredIdPreviousFrame = 0;
4106
HoveredIdPreviousFrameItemCount = 0;
4107
HoveredIdAllowOverlap = false;
4108
HoveredIdIsDisabled = false;
4109
HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
4110
ItemUnclipByLog = false;
4111
ActiveId = 0;
4112
ActiveIdIsAlive = 0;
4113
ActiveIdTimer = 0.0f;
4114
ActiveIdIsJustActivated = false;
4115
ActiveIdAllowOverlap = false;
4116
ActiveIdNoClearOnFocusLoss = false;
4117
ActiveIdHasBeenPressedBefore = false;
4118
ActiveIdHasBeenEditedBefore = false;
4119
ActiveIdHasBeenEditedThisFrame = false;
4120
ActiveIdFromShortcut = false;
4121
ActiveIdClickOffset = ImVec2(-1, -1);
4122
ActiveIdWindow = NULL;
4123
ActiveIdSource = ImGuiInputSource_None;
4124
ActiveIdMouseButton = -1;
4125
ActiveIdPreviousFrame = 0;
4126
memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData));
4127
memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
4128
LastActiveId = 0;
4129
LastActiveIdTimer = 0.0f;
4130
4131
LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
4132
4133
ActiveIdUsingNavDirMask = 0x00;
4134
ActiveIdUsingAllKeyboardKeys = false;
4135
4136
CurrentFocusScopeId = 0;
4137
CurrentItemFlags = ImGuiItemFlags_None;
4138
DebugShowGroupRects = false;
4139
4140
NavCursorVisible = false;
4141
NavHighlightItemUnderNav = false;
4142
NavMousePosDirty = false;
4143
NavIdIsAlive = false;
4144
NavId = 0;
4145
NavWindow = NULL;
4146
NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
4147
NavLayer = ImGuiNavLayer_Main;
4148
NavNextActivateId = 0;
4149
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
4150
NavHighlightActivatedId = 0;
4151
NavHighlightActivatedTimer = 0.0f;
4152
NavInputSource = ImGuiInputSource_Keyboard;
4153
NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
4154
NavCursorHideFrames = 0;
4155
4156
NavAnyRequest = false;
4157
NavInitRequest = false;
4158
NavInitRequestFromMove = false;
4159
NavMoveSubmitted = false;
4160
NavMoveScoringItems = false;
4161
NavMoveForwardToNextFrame = false;
4162
NavMoveFlags = ImGuiNavMoveFlags_None;
4163
NavMoveScrollFlags = ImGuiScrollFlags_None;
4164
NavMoveKeyMods = ImGuiMod_None;
4165
NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
4166
NavScoringDebugCount = 0;
4167
NavTabbingDir = 0;
4168
NavTabbingCounter = 0;
4169
4170
NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
4171
NavJustMovedToKeyMods = ImGuiMod_None;
4172
NavJustMovedToIsTabbing = false;
4173
NavJustMovedToHasSelectionData = false;
4174
4175
// All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
4176
// FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
4177
ConfigNavWindowingWithGamepad = true;
4178
ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
4179
ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
4180
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
4181
NavWindowingInputSource = ImGuiInputSource_None;
4182
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
4183
NavWindowingToggleLayer = false;
4184
NavWindowingToggleKey = ImGuiKey_None;
4185
4186
DimBgRatio = 0.0f;
4187
4188
DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
4189
DragDropSourceFlags = ImGuiDragDropFlags_None;
4190
DragDropSourceFrameCount = -1;
4191
DragDropMouseButton = -1;
4192
DragDropTargetId = 0;
4193
DragDropAcceptFlags = ImGuiDragDropFlags_None;
4194
DragDropAcceptIdCurrRectSurface = 0.0f;
4195
DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
4196
DragDropAcceptFrameCount = -1;
4197
DragDropHoldJustPressedId = 0;
4198
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
4199
4200
ClipperTempDataStacked = 0;
4201
4202
CurrentTable = NULL;
4203
TablesTempDataStacked = 0;
4204
CurrentTabBar = NULL;
4205
CurrentMultiSelect = NULL;
4206
MultiSelectTempDataStacked = 0;
4207
4208
HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
4209
HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
4210
4211
MouseCursor = ImGuiMouseCursor_Arrow;
4212
MouseStationaryTimer = 0.0f;
4213
4214
InputTextPasswordFontBackupFlags = ImFontFlags_None;
4215
TempInputId = 0;
4216
memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
4217
BeginMenuDepth = BeginComboDepth = 0;
4218
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
4219
ColorEditCurrentID = ColorEditSavedID = 0;
4220
ColorEditSavedHue = ColorEditSavedSat = 0.0f;
4221
ColorEditSavedColor = 0;
4222
WindowResizeRelativeMode = false;
4223
ScrollbarHeld = false;
4224
ScrollbarSeekMode = 0;
4225
ScrollbarClickDeltaToGrabCenter = 0.0f;
4226
SliderGrabClickOffset = 0.0f;
4227
SliderCurrentAccum = 0.0f;
4228
SliderCurrentAccumDirty = false;
4229
DragCurrentAccumDirty = false;
4230
DragCurrentAccum = 0.0f;
4231
DragSpeedDefaultRatio = 1.0f / 100.0f;
4232
DisabledAlphaBackup = 0.0f;
4233
DisabledStackSize = 0;
4234
TooltipOverrideCount = 0;
4235
TooltipPreviousWindow = NULL;
4236
4237
PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
4238
PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
4239
4240
SettingsLoaded = false;
4241
SettingsDirtyTimer = 0.0f;
4242
HookIdNext = 0;
4243
4244
memset(LocalizationTable, 0, sizeof(LocalizationTable));
4245
4246
LogEnabled = false;
4247
LogFlags = ImGuiLogFlags_None;
4248
LogWindow = NULL;
4249
LogNextPrefix = LogNextSuffix = NULL;
4250
LogFile = NULL;
4251
LogLinePosY = FLT_MAX;
4252
LogLineFirstItem = false;
4253
LogDepthRef = 0;
4254
LogDepthToExpand = LogDepthToExpandDefault = 2;
4255
4256
ErrorCallback = NULL;
4257
ErrorCallbackUserData = NULL;
4258
ErrorFirst = true;
4259
ErrorCountCurrentFrame = 0;
4260
StackSizesInBeginForCurrentWindow = NULL;
4261
4262
DebugDrawIdConflictsCount = 0;
4263
DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
4264
DebugLocateId = 0;
4265
DebugLogSkippedErrors = 0;
4266
DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
4267
DebugLogAutoDisableFrames = 0;
4268
DebugLocateFrames = 0;
4269
DebugBeginReturnValueCullDepth = -1;
4270
DebugItemPickerActive = false;
4271
DebugItemPickerMouseButton = ImGuiMouseButton_Left;
4272
DebugItemPickerBreakId = 0;
4273
DebugFlashStyleColorTime = 0.0f;
4274
DebugFlashStyleColorIdx = ImGuiCol_COUNT;
4275
4276
// Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
4277
DebugBreakInWindow = 0;
4278
DebugBreakInTable = 0;
4279
DebugBreakInLocateId = false;
4280
DebugBreakKeyChord = ImGuiKey_Pause;
4281
DebugBreakInShortcutRouting = ImGuiKey_None;
4282
4283
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
4284
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
4285
FramerateSecPerFrameAccum = 0.0f;
4286
WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
4287
memset(TempKeychordName, 0, sizeof(TempKeychordName));
4288
}
4289
4290
void ImGui::Initialize()
4291
{
4292
ImGuiContext& g = *GImGui;
4293
IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4294
4295
// Add .ini handle for ImGuiWindow and ImGuiTable types
4296
{
4297
ImGuiSettingsHandler ini_handler;
4298
ini_handler.TypeName = "Window";
4299
ini_handler.TypeHash = ImHashStr("Window");
4300
ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4301
ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4302
ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4303
ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4304
ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4305
AddSettingsHandler(&ini_handler);
4306
}
4307
TableSettingsAddSettingsHandler();
4308
4309
// Setup default localization table
4310
LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
4311
4312
// Setup default ImGuiPlatformIO clipboard/IME handlers.
4313
g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
4314
g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;
4315
g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
4316
g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
4317
4318
// Create default viewport
4319
ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4320
viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4321
g.Viewports.push_back(viewport);
4322
g.TempBuffer.resize(1024 * 3 + 1, 0);
4323
4324
// Build KeysMayBeCharInput[] lookup table (1 bool per named key)
4325
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
4326
if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
4327
|| key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
4328
|| key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
4329
|| key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
4330
g.KeysMayBeCharInput.SetBit(key);
4331
4332
#ifdef IMGUI_HAS_DOCK
4333
#endif
4334
4335
// ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context.
4336
// But this link allows us to facilitate/handle a few edge cases better.
4337
ImFontAtlas* atlas = g.IO.Fonts;
4338
g.DrawListSharedData.Context = &g;
4339
RegisterFontAtlas(atlas);
4340
4341
g.Initialized = true;
4342
}
4343
4344
// This function is merely here to free heap allocations.
4345
void ImGui::Shutdown()
4346
{
4347
ImGuiContext& g = *GImGui;
4348
IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
4349
IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
4350
4351
// 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)
4352
for (ImFontAtlas* atlas : g.FontAtlases)
4353
{
4354
UnregisterFontAtlas(atlas);
4355
if (atlas->OwnerContext == &g)
4356
{
4357
atlas->Locked = false;
4358
IM_DELETE(atlas);
4359
}
4360
}
4361
g.DrawListSharedData.TempBuffer.clear();
4362
4363
// Cleanup of other data are conditional on actually having initialized Dear ImGui.
4364
if (!g.Initialized)
4365
return;
4366
4367
// Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4368
if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4369
SaveIniSettingsToDisk(g.IO.IniFilename);
4370
4371
CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4372
4373
// Clear everything else
4374
g.Windows.clear_delete();
4375
g.WindowsFocusOrder.clear();
4376
g.WindowsTempSortBuffer.clear();
4377
g.CurrentWindow = NULL;
4378
g.CurrentWindowStack.clear();
4379
g.WindowsById.Clear();
4380
g.NavWindow = NULL;
4381
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4382
g.ActiveIdWindow = NULL;
4383
g.MovingWindow = NULL;
4384
4385
g.KeysRoutingTable.Clear();
4386
4387
g.ColorStack.clear();
4388
g.StyleVarStack.clear();
4389
g.FontStack.clear();
4390
g.OpenPopupStack.clear();
4391
g.BeginPopupStack.clear();
4392
g.TreeNodeStack.clear();
4393
4394
g.Viewports.clear_delete();
4395
4396
g.TabBars.Clear();
4397
g.CurrentTabBarStack.clear();
4398
g.ShrinkWidthBuffer.clear();
4399
4400
g.ClipperTempData.clear_destruct();
4401
4402
g.Tables.Clear();
4403
g.TablesTempData.clear_destruct();
4404
g.DrawChannelsTempMergeBuffer.clear();
4405
4406
g.MultiSelectStorage.Clear();
4407
g.MultiSelectTempData.clear_destruct();
4408
4409
g.ClipboardHandlerData.clear();
4410
g.MenusIdSubmittedThisFrame.clear();
4411
g.InputTextState.ClearFreeMemory();
4412
g.InputTextDeactivatedState.ClearFreeMemory();
4413
4414
g.SettingsWindows.clear();
4415
g.SettingsHandlers.clear();
4416
4417
if (g.LogFile)
4418
{
4419
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4420
if (g.LogFile != stdout)
4421
#endif
4422
ImFileClose(g.LogFile);
4423
g.LogFile = NULL;
4424
}
4425
g.LogBuffer.clear();
4426
g.DebugLogBuf.clear();
4427
g.DebugLogIndex.clear();
4428
4429
g.Initialized = false;
4430
}
4431
4432
// No specific ordering/dependency support, will see as needed
4433
ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
4434
{
4435
ImGuiContext& g = *ctx;
4436
IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
4437
g.Hooks.push_back(*hook);
4438
g.Hooks.back().HookId = ++g.HookIdNext;
4439
return g.HookIdNext;
4440
}
4441
4442
// Deferred removal, avoiding issue with changing vector while iterating it
4443
void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
4444
{
4445
ImGuiContext& g = *ctx;
4446
IM_ASSERT(hook_id != 0);
4447
for (ImGuiContextHook& hook : g.Hooks)
4448
if (hook.HookId == hook_id)
4449
hook.Type = ImGuiContextHookType_PendingRemoval_;
4450
}
4451
4452
// Call context hooks (used by e.g. test engine)
4453
// We assume a small number of hooks so all stored in same array
4454
void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
4455
{
4456
ImGuiContext& g = *ctx;
4457
for (ImGuiContextHook& hook : g.Hooks)
4458
if (hook.Type == hook_type)
4459
hook.Callback(&g, &hook);
4460
}
4461
4462
//-----------------------------------------------------------------------------
4463
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
4464
//-----------------------------------------------------------------------------
4465
4466
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
4467
ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
4468
{
4469
memset(this, 0, sizeof(*this));
4470
Ctx = ctx;
4471
Name = ImStrdup(name);
4472
NameBufLen = (int)ImStrlen(name) + 1;
4473
ID = ImHashStr(name);
4474
IDStack.push_back(ID);
4475
MoveId = GetID("#MOVE");
4476
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4477
ScrollExpected = ImVec2(0, 0);
4478
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
4479
AutoFitFramesX = AutoFitFramesY = -1;
4480
AutoPosLastDirection = ImGuiDir_None;
4481
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0;
4482
SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
4483
LastFrameActive = -1;
4484
LastTimeActive = -1.0f;
4485
FontRefSize = 0.0f;
4486
FontWindowScale = FontWindowScaleParents = 1.0f;
4487
SettingsOffset = -1;
4488
DrawList = &DrawListInst;
4489
DrawList->_OwnerName = Name;
4490
DrawList->_SetDrawListSharedData(&Ctx->DrawListSharedData);
4491
NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
4492
}
4493
4494
ImGuiWindow::~ImGuiWindow()
4495
{
4496
IM_ASSERT(DrawList == &DrawListInst);
4497
IM_DELETE(Name);
4498
ColumnsStorage.clear_destruct();
4499
}
4500
4501
static void SetCurrentWindow(ImGuiWindow* window)
4502
{
4503
ImGuiContext& g = *GImGui;
4504
g.CurrentWindow = window;
4505
g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
4506
g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
4507
g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
4508
if (window)
4509
{
4510
bool backup_skip_items = window->SkipItems;
4511
window->SkipItems = false;
4512
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
4513
{
4514
ImGuiViewport* viewport = window->Viewport;
4515
g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity()
4516
}
4517
ImGui::UpdateCurrentFontSize(0.0f);
4518
window->SkipItems = backup_skip_items;
4519
ImGui::NavUpdateCurrentWindowIsScrollPushableX();
4520
}
4521
}
4522
4523
void ImGui::GcCompactTransientMiscBuffers()
4524
{
4525
ImGuiContext& g = *GImGui;
4526
g.ItemFlagsStack.clear();
4527
g.GroupStack.clear();
4528
g.MultiSelectTempDataStacked = 0;
4529
g.MultiSelectTempData.clear_destruct();
4530
TableGcCompactSettings();
4531
for (ImFontAtlas* atlas : g.FontAtlases)
4532
atlas->CompactCache();
4533
}
4534
4535
// Free up/compact internal window buffers, we can use this when a window becomes unused.
4536
// Not freed:
4537
// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
4538
// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
4539
void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
4540
{
4541
window->MemoryCompacted = true;
4542
window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
4543
window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
4544
window->IDStack.clear();
4545
window->DrawList->_ClearFreeMemory();
4546
window->DC.ChildWindows.clear();
4547
window->DC.ItemWidthStack.clear();
4548
window->DC.TextWrapPosStack.clear();
4549
}
4550
4551
void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
4552
{
4553
// We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
4554
// The other buffers tends to amortize much faster.
4555
window->MemoryCompacted = false;
4556
window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
4557
window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
4558
window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
4559
}
4560
4561
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
4562
{
4563
ImGuiContext& g = *GImGui;
4564
4565
// Clear previous active id
4566
if (g.ActiveId != 0)
4567
{
4568
// While most behaved code would make an effort to not steal active id during window move/drag operations,
4569
// we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
4570
// may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
4571
if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
4572
{
4573
IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
4574
g.MovingWindow = NULL;
4575
}
4576
4577
// Store deactivate data
4578
ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData;
4579
deactivated_data->ID = g.ActiveId;
4580
deactivated_data->ElapseFrame = (g.LastItemData.ID == g.ActiveId) ? g.FrameCount : g.FrameCount + 1; // FIXME: OK to use LastItemData?
4581
deactivated_data->HasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4582
deactivated_data->IsAlive = (g.ActiveIdIsAlive == g.ActiveId);
4583
4584
// This could be written in a more general way (e.g associate a hook to ActiveId),
4585
// but since this is currently quite an exception we'll leave it as is.
4586
// One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()
4587
if (g.InputTextState.ID == g.ActiveId)
4588
InputTextDeactivateHook(g.ActiveId);
4589
}
4590
4591
// Set active id
4592
g.ActiveIdIsJustActivated = (g.ActiveId != id);
4593
if (g.ActiveIdIsJustActivated)
4594
{
4595
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 : "");
4596
g.ActiveIdTimer = 0.0f;
4597
g.ActiveIdHasBeenPressedBefore = false;
4598
g.ActiveIdHasBeenEditedBefore = false;
4599
g.ActiveIdMouseButton = -1;
4600
if (id != 0)
4601
{
4602
g.LastActiveId = id;
4603
g.LastActiveIdTimer = 0.0f;
4604
}
4605
}
4606
g.ActiveId = id;
4607
g.ActiveIdAllowOverlap = false;
4608
g.ActiveIdNoClearOnFocusLoss = false;
4609
g.ActiveIdWindow = window;
4610
g.ActiveIdHasBeenEditedThisFrame = false;
4611
g.ActiveIdFromShortcut = false;
4612
if (id)
4613
{
4614
g.ActiveIdIsAlive = id;
4615
g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
4616
IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
4617
}
4618
4619
// Clear declaration of inputs claimed by the widget
4620
// (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
4621
g.ActiveIdUsingNavDirMask = 0x00;
4622
g.ActiveIdUsingAllKeyboardKeys = false;
4623
}
4624
4625
void ImGui::ClearActiveID()
4626
{
4627
SetActiveID(0, NULL); // g.ActiveId = 0;
4628
}
4629
4630
void ImGui::SetHoveredID(ImGuiID id)
4631
{
4632
ImGuiContext& g = *GImGui;
4633
g.HoveredId = id;
4634
g.HoveredIdAllowOverlap = false;
4635
if (id != 0 && g.HoveredIdPreviousFrame != id)
4636
g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
4637
}
4638
4639
ImGuiID ImGui::GetHoveredID()
4640
{
4641
ImGuiContext& g = *GImGui;
4642
return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
4643
}
4644
4645
void ImGui::MarkItemEdited(ImGuiID id)
4646
{
4647
// This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
4648
// 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.
4649
ImGuiContext& g = *GImGui;
4650
if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
4651
return;
4652
if (g.ActiveId == id || g.ActiveId == 0)
4653
{
4654
// FIXME: Can't we fully rely on LastItemData yet?
4655
g.ActiveIdHasBeenEditedThisFrame = true;
4656
g.ActiveIdHasBeenEditedBefore = true;
4657
if (g.DeactivatedItemData.ID == id)
4658
g.DeactivatedItemData.HasBeenEditedBefore = true;
4659
}
4660
4661
// We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
4662
// We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
4663
IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
4664
4665
//IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
4666
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
4667
}
4668
4669
bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
4670
{
4671
// An active popup disable hovering on other windows (apart from its own children)
4672
// FIXME-OPT: This could be cached/stored within the window.
4673
ImGuiContext& g = *GImGui;
4674
if (g.NavWindow)
4675
if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
4676
if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
4677
{
4678
// For the purpose of those flags we differentiate "standard popup" from "modal popup"
4679
// NB: The 'else' is important because Modal windows are also Popups.
4680
bool want_inhibit = false;
4681
if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
4682
want_inhibit = true;
4683
else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
4684
want_inhibit = true;
4685
4686
// Inhibit hover unless the window is within the stack of our modal/popup
4687
if (want_inhibit)
4688
if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
4689
return false;
4690
}
4691
return true;
4692
}
4693
4694
static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
4695
{
4696
ImGuiContext& g = *GImGui;
4697
if (flags & ImGuiHoveredFlags_DelayNormal)
4698
return g.Style.HoverDelayNormal;
4699
if (flags & ImGuiHoveredFlags_DelayShort)
4700
return g.Style.HoverDelayShort;
4701
return 0.0f;
4702
}
4703
4704
static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
4705
{
4706
// Allow instance flags to override shared flags
4707
if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
4708
shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
4709
return user_flags | shared_flags;
4710
}
4711
4712
// This is roughly matching the behavior of internal-facing ItemHoverable()
4713
// - 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()
4714
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
4715
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
4716
{
4717
ImGuiContext& g = *GImGui;
4718
ImGuiWindow* window = g.CurrentWindow;
4719
IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
4720
4721
if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
4722
{
4723
if (!IsItemFocused())
4724
return false;
4725
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4726
return false;
4727
4728
if (flags & ImGuiHoveredFlags_ForTooltip)
4729
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
4730
}
4731
else
4732
{
4733
// Test for bounding box overlap, as updated as ItemAdd()
4734
ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
4735
if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
4736
return false;
4737
4738
if (flags & ImGuiHoveredFlags_ForTooltip)
4739
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
4740
4741
// Done with rectangle culling so we can perform heavier checks now
4742
// Test if we are hovering the right window (our window could be behind another window)
4743
// [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
4744
// [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
4745
// 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
4746
// the test that has been running for a long while.
4747
if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
4748
if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
4749
return false;
4750
4751
// Test if another item is active (e.g. being dragged)
4752
const ImGuiID id = g.LastItemData.ID;
4753
if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
4754
if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4755
if (g.ActiveId != window->MoveId)
4756
return false;
4757
4758
// Test if interactions on this window are blocked by an active popup or modal.
4759
// The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
4760
if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
4761
return false;
4762
4763
// Test if the item is disabled
4764
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4765
return false;
4766
4767
// Special handling for calling after Begin() which represent the title bar or tab.
4768
// When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
4769
// will never be overwritten so we need to detect the case.
4770
if (id == window->MoveId && window->WriteAccessed)
4771
return false;
4772
4773
// Test if using AllowOverlap and overlapped
4774
if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
4775
if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
4776
if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
4777
return false;
4778
}
4779
4780
// Handle hover delay
4781
// (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
4782
const float delay = CalcDelayFromHoveredFlags(flags);
4783
if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
4784
{
4785
ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min);
4786
if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
4787
g.HoverItemDelayTimer = 0.0f;
4788
g.HoverItemDelayId = hover_delay_id;
4789
4790
// When changing hovered item we requires a bit of stationary delay before activating hover timer,
4791
// but once unlocked on a given item we also moving.
4792
//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); }
4793
if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
4794
return false;
4795
4796
if (g.HoverItemDelayTimer < delay)
4797
return false;
4798
}
4799
4800
return true;
4801
}
4802
4803
// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
4804
// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
4805
// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
4806
// If you used this in your legacy/custom widgets code:
4807
// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
4808
// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
4809
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
4810
{
4811
ImGuiContext& g = *GImGui;
4812
ImGuiWindow* window = g.CurrentWindow;
4813
4814
// Detect ID conflicts
4815
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4816
if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
4817
{
4818
g.HoveredIdPreviousFrameItemCount++;
4819
if (g.DebugDrawIdConflicts == id)
4820
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);
4821
}
4822
#endif
4823
4824
if (g.HoveredWindow != window)
4825
return false;
4826
if (!IsMouseHoveringRect(bb.Min, bb.Max))
4827
return false;
4828
4829
if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
4830
return false;
4831
if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4832
if (!g.ActiveIdFromShortcut)
4833
return false;
4834
4835
// Done with rectangle culling so we can perform heavier checks now.
4836
if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
4837
{
4838
g.HoveredIdIsDisabled = true;
4839
return false;
4840
}
4841
4842
// We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
4843
// hover test in widgets code. We could also decide to split this function is two.
4844
if (id != 0)
4845
{
4846
// Drag source doesn't report as hovered
4847
if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
4848
return false;
4849
4850
SetHoveredID(id);
4851
4852
// AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
4853
// This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
4854
if (item_flags & ImGuiItemFlags_AllowOverlap)
4855
{
4856
g.HoveredIdAllowOverlap = true;
4857
if (g.HoveredIdPreviousFrame != id)
4858
return false;
4859
}
4860
4861
// Display shortcut (only works with mouse)
4862
// (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
4863
if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
4864
if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
4865
SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
4866
}
4867
4868
// When disabled we'll return false but still set HoveredId
4869
if (item_flags & ImGuiItemFlags_Disabled)
4870
{
4871
// Release active id if turning disabled
4872
if (g.ActiveId == id && id != 0)
4873
ClearActiveID();
4874
g.HoveredIdIsDisabled = true;
4875
return false;
4876
}
4877
4878
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4879
if (id != 0)
4880
{
4881
// [DEBUG] Item Picker tool!
4882
// We perform the check here because reaching is path is rare (1~ time a frame),
4883
// making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
4884
// items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
4885
if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
4886
GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
4887
if (g.DebugItemPickerBreakId == id)
4888
IM_DEBUG_BREAK();
4889
}
4890
#endif
4891
4892
if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
4893
return false;
4894
4895
return true;
4896
}
4897
4898
// FIXME: This is inlined/duplicated in ItemAdd()
4899
// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
4900
bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
4901
{
4902
ImGuiContext& g = *GImGui;
4903
ImGuiWindow* window = g.CurrentWindow;
4904
if (!bb.Overlaps(window->ClipRect))
4905
if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
4906
if (!g.ItemUnclipByLog)
4907
return true;
4908
return false;
4909
}
4910
4911
// This is also inlined in ItemAdd()
4912
// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
4913
void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect)
4914
{
4915
ImGuiContext& g = *GImGui;
4916
g.LastItemData.ID = item_id;
4917
g.LastItemData.ItemFlags = item_flags;
4918
g.LastItemData.StatusFlags = status_flags;
4919
g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
4920
}
4921
4922
static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
4923
{
4924
ImGuiContext& g = *GImGui;
4925
SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect);
4926
}
4927
4928
static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect)
4929
{
4930
ImGuiContext& g = *GImGui;
4931
SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect);
4932
}
4933
4934
float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
4935
{
4936
if (wrap_pos_x < 0.0f)
4937
return 0.0f;
4938
4939
ImGuiContext& g = *GImGui;
4940
ImGuiWindow* window = g.CurrentWindow;
4941
if (wrap_pos_x == 0.0f)
4942
{
4943
// We could decide to setup a default wrapping max point for auto-resizing windows,
4944
// or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
4945
//if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
4946
// wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
4947
//else
4948
wrap_pos_x = window->WorkRect.Max.x;
4949
}
4950
else if (wrap_pos_x > 0.0f)
4951
{
4952
wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
4953
}
4954
4955
return ImMax(wrap_pos_x - pos.x, 1.0f);
4956
}
4957
4958
// IM_ALLOC() == ImGui::MemAlloc()
4959
void* ImGui::MemAlloc(size_t size)
4960
{
4961
void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
4962
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4963
if (ImGuiContext* ctx = GImGui)
4964
DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
4965
#endif
4966
return ptr;
4967
}
4968
4969
// IM_FREE() == ImGui::MemFree()
4970
void ImGui::MemFree(void* ptr)
4971
{
4972
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4973
if (ptr != NULL)
4974
if (ImGuiContext* ctx = GImGui)
4975
DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
4976
#endif
4977
return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
4978
}
4979
4980
// 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"
4981
void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
4982
{
4983
ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4984
IM_UNUSED(ptr);
4985
if (entry->FrameCount != frame_count)
4986
{
4987
info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
4988
entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4989
entry->FrameCount = frame_count;
4990
entry->AllocCount = entry->FreeCount = 0;
4991
}
4992
if (size != (size_t)-1)
4993
{
4994
//printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, (int)size, ptr);
4995
entry->AllocCount++;
4996
info->TotalAllocCount++;
4997
}
4998
else
4999
{
5000
//printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
5001
entry->FreeCount++;
5002
info->TotalFreeCount++;
5003
}
5004
}
5005
5006
const char* ImGui::GetClipboardText()
5007
{
5008
ImGuiContext& g = *GImGui;
5009
return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : "";
5010
}
5011
5012
void ImGui::SetClipboardText(const char* text)
5013
{
5014
ImGuiContext& g = *GImGui;
5015
if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
5016
g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
5017
}
5018
5019
const char* ImGui::GetVersion()
5020
{
5021
return IMGUI_VERSION;
5022
}
5023
5024
ImGuiIO& ImGui::GetIO()
5025
{
5026
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5027
return GImGui->IO;
5028
}
5029
5030
// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
5031
ImGuiIO& ImGui::GetIO(ImGuiContext* ctx)
5032
{
5033
IM_ASSERT(ctx != NULL);
5034
return ctx->IO;
5035
}
5036
5037
ImGuiPlatformIO& ImGui::GetPlatformIO()
5038
{
5039
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
5040
return GImGui->PlatformIO;
5041
}
5042
5043
// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
5044
ImGuiPlatformIO& ImGui::GetPlatformIO(ImGuiContext* ctx)
5045
{
5046
IM_ASSERT(ctx != NULL);
5047
return ctx->PlatformIO;
5048
}
5049
5050
// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
5051
ImDrawData* ImGui::GetDrawData()
5052
{
5053
ImGuiContext& g = *GImGui;
5054
ImGuiViewportP* viewport = g.Viewports[0];
5055
return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
5056
}
5057
5058
double ImGui::GetTime()
5059
{
5060
return GImGui->Time;
5061
}
5062
5063
int ImGui::GetFrameCount()
5064
{
5065
return GImGui->FrameCount;
5066
}
5067
5068
static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
5069
{
5070
// Create the draw list on demand, because they are not frequently used for all viewports
5071
ImGuiContext& g = *GImGui;
5072
IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
5073
ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
5074
if (draw_list == NULL)
5075
{
5076
draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
5077
draw_list->_OwnerName = drawlist_name;
5078
viewport->BgFgDrawLists[drawlist_no] = draw_list;
5079
}
5080
5081
// Our ImDrawList system requires that there is always a command
5082
if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
5083
{
5084
draw_list->_ResetForNewFrame();
5085
draw_list->PushTexture(g.IO.Fonts->TexRef);
5086
draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
5087
viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
5088
}
5089
return draw_list;
5090
}
5091
5092
ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
5093
{
5094
return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
5095
}
5096
5097
ImDrawList* ImGui::GetBackgroundDrawList()
5098
{
5099
ImGuiContext& g = *GImGui;
5100
return GetBackgroundDrawList(g.Viewports[0]);
5101
}
5102
5103
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
5104
{
5105
return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
5106
}
5107
5108
ImDrawList* ImGui::GetForegroundDrawList()
5109
{
5110
ImGuiContext& g = *GImGui;
5111
return GetForegroundDrawList(g.Viewports[0]);
5112
}
5113
5114
ImDrawListSharedData* ImGui::GetDrawListSharedData()
5115
{
5116
return &GImGui->DrawListSharedData;
5117
}
5118
5119
void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
5120
{
5121
// Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
5122
// We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
5123
// This is because we want ActiveId to be set even when the window is not permitted to move.
5124
ImGuiContext& g = *GImGui;
5125
FocusWindow(window);
5126
SetActiveID(window->MoveId, window);
5127
if (g.IO.ConfigNavCursorVisibleAuto)
5128
g.NavCursorVisible = false;
5129
g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
5130
g.ActiveIdNoClearOnFocusLoss = true;
5131
SetActiveIdUsingAllKeyboardKeys();
5132
5133
bool can_move_window = true;
5134
if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
5135
can_move_window = false;
5136
if (can_move_window)
5137
g.MovingWindow = window;
5138
}
5139
5140
// Handle mouse moving window
5141
// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
5142
// FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId.
5143
// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
5144
// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
5145
void ImGui::UpdateMouseMovingWindowNewFrame()
5146
{
5147
ImGuiContext& g = *GImGui;
5148
if (g.MovingWindow != NULL)
5149
{
5150
// We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
5151
// We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
5152
KeepAliveID(g.ActiveId);
5153
IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
5154
ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
5155
if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
5156
{
5157
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
5158
SetWindowPos(moving_window, pos, ImGuiCond_Always);
5159
FocusWindow(g.MovingWindow);
5160
}
5161
else
5162
{
5163
g.MovingWindow = NULL;
5164
ClearActiveID();
5165
}
5166
}
5167
else
5168
{
5169
// When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
5170
if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
5171
{
5172
KeepAliveID(g.ActiveId);
5173
if (!g.IO.MouseDown[0])
5174
ClearActiveID();
5175
}
5176
}
5177
}
5178
5179
// Initiate focusing and moving window when clicking on empty space or title bar.
5180
// Initiate focusing window when clicking on a disabled item.
5181
// Handle left-click and right-click focus.
5182
void ImGui::UpdateMouseMovingWindowEndFrame()
5183
{
5184
ImGuiContext& g = *GImGui;
5185
if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
5186
return;
5187
5188
// Unless we just made a window/popup appear
5189
if (g.NavWindow && g.NavWindow->Appearing)
5190
return;
5191
5192
// Click on empty space to focus window and start moving
5193
// (after we're done with all our widgets)
5194
if (g.IO.MouseClicked[0])
5195
{
5196
// Handle the edge case of a popup being closed while clicking in its empty space.
5197
// If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
5198
ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
5199
const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
5200
5201
if (root_window != NULL && !is_closed_popup)
5202
{
5203
StartMouseMovingWindow(g.HoveredWindow); //-V595
5204
5205
// Cancel moving if clicked outside of title bar
5206
if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
5207
if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
5208
if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
5209
g.MovingWindow = NULL;
5210
5211
// Cancel moving if clicked over an item which was disabled or inhibited by popups
5212
// (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)0 already)
5213
if (g.HoveredIdIsDisabled)
5214
g.MovingWindow = NULL;
5215
}
5216
else if (root_window == NULL && g.NavWindow != NULL)
5217
{
5218
// Clicking on void disable focus
5219
FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
5220
}
5221
}
5222
5223
// With right mouse button we close popups without changing focus based on where the mouse is aimed
5224
// Instead, focus will be restored to the window under the bottom-most closed popup.
5225
// (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
5226
if (g.IO.MouseClicked[1] && g.HoveredId == 0)
5227
{
5228
// Find the top-most window between HoveredWindow and the top-most Modal Window.
5229
// This is where we can trim the popup stack.
5230
ImGuiWindow* modal = GetTopMostPopupModal();
5231
bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));
5232
ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
5233
}
5234
}
5235
5236
static bool IsWindowActiveAndVisible(ImGuiWindow* window)
5237
{
5238
return (window->Active) && (!window->Hidden);
5239
}
5240
5241
// 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)
5242
void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)
5243
{
5244
ImGuiContext& g = *GImGui;
5245
ImGuiIO& io = g.IO;
5246
5247
// FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
5248
// by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
5249
g.WindowsBorderHoverPadding = ImMax(ImMax(g.Style.TouchExtraPadding.x, g.Style.TouchExtraPadding.y), g.Style.WindowBorderHoverPadding);
5250
5251
// Find the window hovered by mouse:
5252
// - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
5253
// - 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.
5254
// - 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.
5255
bool clear_hovered_windows = false;
5256
FindHoveredWindowEx(mouse_pos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
5257
g.HoveredWindowBeforeClear = g.HoveredWindow;
5258
5259
// Modal windows prevents mouse from hovering behind them.
5260
ImGuiWindow* modal_window = GetTopMostPopupModal();
5261
if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))
5262
clear_hovered_windows = true;
5263
5264
// Disabled mouse hovering (we don't currently clear MousePos, we could)
5265
if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
5266
clear_hovered_windows = true;
5267
5268
// We track click ownership. When clicked outside of a window the click is owned by the application and
5269
// won't report hovering nor request capture even while dragging over our windows afterward.
5270
const bool has_open_popup = (g.OpenPopupStack.Size > 0);
5271
const bool has_open_modal = (modal_window != NULL);
5272
int mouse_earliest_down = -1;
5273
bool mouse_any_down = false;
5274
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
5275
{
5276
if (io.MouseClicked[i])
5277
{
5278
io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
5279
io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
5280
}
5281
mouse_any_down |= io.MouseDown[i];
5282
if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
5283
if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
5284
mouse_earliest_down = i;
5285
}
5286
const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
5287
const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
5288
5289
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
5290
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
5291
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
5292
if (!mouse_avail && !mouse_dragging_extern_payload)
5293
clear_hovered_windows = true;
5294
5295
if (clear_hovered_windows)
5296
g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
5297
5298
// Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
5299
// Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
5300
if (g.WantCaptureMouseNextFrame != -1)
5301
{
5302
io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
5303
}
5304
else
5305
{
5306
io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
5307
io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
5308
}
5309
5310
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
5311
io.WantCaptureKeyboard = false;
5312
if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
5313
{
5314
if ((g.ActiveId != 0) || (modal_window != NULL))
5315
io.WantCaptureKeyboard = true;
5316
else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
5317
io.WantCaptureKeyboard = true;
5318
}
5319
if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
5320
io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
5321
5322
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
5323
io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
5324
}
5325
5326
static void ImGui::UpdateTexturesNewFrame()
5327
{
5328
// Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count.
5329
ImGuiContext& g = *GImGui;
5330
const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
5331
for (ImFontAtlas* atlas : g.FontAtlases)
5332
{
5333
if (atlas->OwnerContext == &g)
5334
{
5335
ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures);
5336
}
5337
else
5338
{
5339
// (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it.
5340
// Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context.
5341
// (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that.
5342
// (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures.
5343
IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1);
5344
IM_ASSERT(atlas->RendererHasTextures == has_textures);
5345
}
5346
}
5347
}
5348
5349
// Build a single texture list
5350
static void ImGui::UpdateTexturesEndFrame()
5351
{
5352
ImGuiContext& g = *GImGui;
5353
g.PlatformIO.Textures.resize(0);
5354
for (ImFontAtlas* atlas : g.FontAtlases)
5355
for (ImTextureData* tex : atlas->TexList)
5356
{
5357
// We provide this information so backends can decide whether to destroy textures.
5358
// This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized.
5359
tex->RefCount = (unsigned short)atlas->RefCount;
5360
g.PlatformIO.Textures.push_back(tex);
5361
}
5362
for (ImTextureData* tex : g.UserTextures)
5363
g.PlatformIO.Textures.push_back(tex);
5364
}
5365
5366
// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
5367
// FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
5368
static void SetupDrawListSharedData()
5369
{
5370
ImGuiContext& g = *GImGui;
5371
ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
5372
for (ImGuiViewportP* viewport : g.Viewports)
5373
virtual_space.Add(viewport->GetMainRect());
5374
g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
5375
g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
5376
g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
5377
g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
5378
if (g.Style.AntiAliasedLines)
5379
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
5380
if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
5381
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
5382
if (g.Style.AntiAliasedFill)
5383
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
5384
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
5385
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
5386
g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments.
5387
}
5388
5389
void ImGui::NewFrame()
5390
{
5391
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5392
ImGuiContext& g = *GImGui;
5393
5394
// Remove pending delete hooks before frame start.
5395
// This deferred removal avoid issues of removal while iterating the hook vector
5396
for (int n = g.Hooks.Size - 1; n >= 0; n--)
5397
if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
5398
g.Hooks.erase(&g.Hooks[n]);
5399
5400
CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
5401
5402
// Check and assert for various common IO and Configuration mistakes
5403
ErrorCheckNewFrameSanityChecks();
5404
5405
// Load settings on first frame, save settings when modified (after a delay)
5406
UpdateSettings();
5407
5408
g.Time += g.IO.DeltaTime;
5409
g.FrameCount += 1;
5410
g.TooltipOverrideCount = 0;
5411
g.WindowsActiveCount = 0;
5412
g.MenusIdSubmittedThisFrame.resize(0);
5413
5414
// Calculate frame-rate for the user, as a purely luxurious feature
5415
g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
5416
g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
5417
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
5418
g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
5419
g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
5420
5421
// Process input queue (trickle as many events as possible), turn events into writes to IO structure
5422
g.InputEventsTrail.resize(0);
5423
UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
5424
5425
// Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
5426
UpdateViewportsNewFrame();
5427
5428
// Update texture list (collect destroyed textures, etc.)
5429
UpdateTexturesNewFrame();
5430
5431
// Setup current font and draw list shared data
5432
SetupDrawListSharedData();
5433
UpdateFontsNewFrame();
5434
5435
g.WithinFrameScope = true;
5436
5437
// Mark rendering data as invalid to prevent user who may have a handle on it to use it.
5438
for (ImGuiViewportP* viewport : g.Viewports)
5439
viewport->DrawDataP.Valid = false;
5440
5441
// Drag and drop keep the source ID alive so even if the source disappear our state is consistent
5442
if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
5443
KeepAliveID(g.DragDropPayload.SourceId);
5444
5445
// [DEBUG]
5446
if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
5447
g.DebugDrawIdConflicts = 0;
5448
if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
5449
g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame;
5450
5451
// Update HoveredId data
5452
if (!g.HoveredIdPreviousFrame)
5453
g.HoveredIdTimer = 0.0f;
5454
if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
5455
g.HoveredIdNotActiveTimer = 0.0f;
5456
if (g.HoveredId)
5457
g.HoveredIdTimer += g.IO.DeltaTime;
5458
if (g.HoveredId && g.ActiveId != g.HoveredId)
5459
g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
5460
g.HoveredIdPreviousFrame = g.HoveredId;
5461
g.HoveredIdPreviousFrameItemCount = 0;
5462
g.HoveredId = 0;
5463
g.HoveredIdAllowOverlap = false;
5464
g.HoveredIdIsDisabled = false;
5465
g.ScrollbarHeld >>= 1;
5466
5467
// Clear ActiveID if the item is not alive anymore.
5468
// In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
5469
// As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
5470
if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
5471
{
5472
IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
5473
ClearActiveID();
5474
}
5475
5476
// Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
5477
if (g.ActiveId)
5478
g.ActiveIdTimer += g.IO.DeltaTime;
5479
g.LastActiveIdTimer += g.IO.DeltaTime;
5480
g.ActiveIdPreviousFrame = g.ActiveId;
5481
g.ActiveIdIsAlive = 0;
5482
g.ActiveIdHasBeenEditedThisFrame = false;
5483
g.ActiveIdIsJustActivated = false;
5484
if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
5485
g.TempInputId = 0;
5486
if (g.ActiveId == 0)
5487
{
5488
g.ActiveIdUsingNavDirMask = 0x00;
5489
g.ActiveIdUsingAllKeyboardKeys = false;
5490
}
5491
if (g.DeactivatedItemData.ElapseFrame < g.FrameCount)
5492
g.DeactivatedItemData.ID = 0;
5493
g.DeactivatedItemData.IsAlive = false;
5494
5495
// Record when we have been stationary as this state is preserved while over same item.
5496
// FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
5497
// To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
5498
if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5499
g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
5500
else if (g.HoverItemDelayId == 0)
5501
g.HoverItemUnlockedStationaryId = 0;
5502
if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5503
g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
5504
else if (g.HoveredWindow == NULL)
5505
g.HoverWindowUnlockedStationaryId = 0;
5506
5507
// Update hover delay for IsItemHovered() with delays and tooltips
5508
g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
5509
if (g.HoverItemDelayId != 0)
5510
{
5511
g.HoverItemDelayTimer += g.IO.DeltaTime;
5512
g.HoverItemDelayClearTimer = 0.0f;
5513
g.HoverItemDelayId = 0;
5514
}
5515
else if (g.HoverItemDelayTimer > 0.0f)
5516
{
5517
// This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
5518
// We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
5519
g.HoverItemDelayClearTimer += g.IO.DeltaTime;
5520
if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
5521
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.
5522
}
5523
5524
// Drag and drop
5525
g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
5526
g.DragDropAcceptIdCurr = 0;
5527
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
5528
g.DragDropWithinSource = false;
5529
g.DragDropWithinTarget = false;
5530
g.DragDropHoldJustPressedId = 0;
5531
g.TooltipPreviousWindow = NULL;
5532
5533
// Close popups on focus lost (currently wip/opt-in)
5534
//if (g.IO.AppFocusLost)
5535
// ClosePopupsExceptModals();
5536
5537
// Update keyboard input state
5538
UpdateKeyboardInputs();
5539
5540
//IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
5541
//IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
5542
//IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
5543
//IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
5544
5545
// Update keyboard/gamepad navigation
5546
NavUpdate();
5547
5548
// Update mouse input state
5549
UpdateMouseInputs();
5550
5551
// Mark all windows as not visible and compact unused memory.
5552
IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
5553
const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
5554
for (ImGuiWindow* window : g.Windows)
5555
{
5556
window->WasActive = window->Active;
5557
window->Active = false;
5558
window->WriteAccessed = false;
5559
window->BeginCountPreviousFrame = window->BeginCount;
5560
window->BeginCount = 0;
5561
5562
// Garbage collect transient buffers of recently unused windows
5563
if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
5564
GcCompactTransientWindowBuffers(window);
5565
}
5566
5567
// Find hovered window
5568
// (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
5569
// (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
5570
UpdateHoveredWindowAndCaptureFlags(g.IO.MousePos);
5571
5572
// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
5573
UpdateMouseMovingWindowNewFrame();
5574
5575
// Background darkening/whitening
5576
if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
5577
g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
5578
else
5579
g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
5580
5581
g.MouseCursor = ImGuiMouseCursor_Arrow;
5582
g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
5583
5584
// Platform IME data: reset for the frame
5585
g.PlatformImeDataPrev = g.PlatformImeData;
5586
g.PlatformImeData.WantVisible = g.PlatformImeData.WantTextInput = false;
5587
5588
// Mouse wheel scrolling, scale
5589
UpdateMouseWheel();
5590
5591
// Garbage collect transient buffers of recently unused tables
5592
for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
5593
if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
5594
TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
5595
for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
5596
if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
5597
TableGcCompactTransientBuffers(&table_temp_data);
5598
if (g.GcCompactAll)
5599
GcCompactTransientMiscBuffers();
5600
g.GcCompactAll = false;
5601
5602
// Closing the focused window restore focus to the first active root window in descending z-order
5603
if (g.NavWindow && !g.NavWindow->WasActive)
5604
FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
5605
5606
// No window should be open at the beginning of the frame.
5607
// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
5608
g.CurrentWindowStack.resize(0);
5609
g.BeginPopupStack.resize(0);
5610
g.ItemFlagsStack.resize(0);
5611
g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
5612
g.CurrentItemFlags = g.ItemFlagsStack.back();
5613
g.GroupStack.resize(0);
5614
5615
// [DEBUG] Update debug features
5616
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5617
UpdateDebugToolItemPicker();
5618
UpdateDebugToolStackQueries();
5619
UpdateDebugToolFlashStyleColor();
5620
if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
5621
{
5622
g.DebugLocateId = 0;
5623
g.DebugBreakInLocateId = false;
5624
}
5625
if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
5626
{
5627
DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
5628
g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
5629
g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
5630
}
5631
#endif
5632
5633
// Create implicit/fallback window - which we will only render it if the user has added something to it.
5634
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
5635
// This fallback is particularly important as it prevents ImGui:: calls from crashing.
5636
g.WithinFrameScopeWithImplicitWindow = true;
5637
SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
5638
Begin("Debug##Default");
5639
IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
5640
5641
// Store stack sizes
5642
g.ErrorCountCurrentFrame = 0;
5643
ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
5644
5645
// [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
5646
// allowing to validate correct Begin/End behavior in user code.
5647
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5648
if (g.IO.ConfigDebugBeginReturnValueLoop)
5649
g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
5650
else
5651
g.DebugBeginReturnValueCullDepth = -1;
5652
#endif
5653
5654
CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
5655
}
5656
5657
// FIXME: Add a more explicit sort order in the window structure.
5658
static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
5659
{
5660
const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
5661
const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
5662
if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
5663
return d;
5664
if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
5665
return d;
5666
return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
5667
}
5668
5669
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
5670
{
5671
out_sorted_windows->push_back(window);
5672
if (window->Active)
5673
{
5674
int count = window->DC.ChildWindows.Size;
5675
ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
5676
for (int i = 0; i < count; i++)
5677
{
5678
ImGuiWindow* child = window->DC.ChildWindows[i];
5679
if (child->Active)
5680
AddWindowToSortBuffer(out_sorted_windows, child);
5681
}
5682
}
5683
}
5684
5685
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
5686
{
5687
ImGuiContext& g = *GImGui;
5688
ImGuiViewportP* viewport = g.Viewports[0];
5689
g.IO.MetricsRenderWindows++;
5690
if (window->DrawList->_Splitter._Count > 1)
5691
window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
5692
ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
5693
for (ImGuiWindow* child : window->DC.ChildWindows)
5694
if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
5695
AddWindowToDrawData(child, layer);
5696
}
5697
5698
static inline int GetWindowDisplayLayer(ImGuiWindow* window)
5699
{
5700
return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
5701
}
5702
5703
// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
5704
static inline void AddRootWindowToDrawData(ImGuiWindow* window)
5705
{
5706
AddWindowToDrawData(window, GetWindowDisplayLayer(window));
5707
}
5708
5709
static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
5710
{
5711
int n = builder->Layers[0]->Size;
5712
int full_size = n;
5713
for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
5714
full_size += builder->Layers[i]->Size;
5715
builder->Layers[0]->resize(full_size);
5716
for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
5717
{
5718
ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
5719
if (layer->empty())
5720
continue;
5721
memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
5722
n += layer->Size;
5723
layer->resize(0);
5724
}
5725
}
5726
5727
static void InitViewportDrawData(ImGuiViewportP* viewport)
5728
{
5729
ImGuiIO& io = ImGui::GetIO();
5730
ImDrawData* draw_data = &viewport->DrawDataP;
5731
5732
viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
5733
viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
5734
viewport->DrawDataBuilder.Layers[0]->resize(0);
5735
viewport->DrawDataBuilder.Layers[1]->resize(0);
5736
5737
draw_data->Valid = true;
5738
draw_data->CmdListsCount = 0;
5739
draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
5740
draw_data->DisplayPos = viewport->Pos;
5741
draw_data->DisplaySize = viewport->Size;
5742
draw_data->FramebufferScale = io.DisplayFramebufferScale;
5743
draw_data->OwnerViewport = viewport;
5744
draw_data->Textures = &ImGui::GetPlatformIO().Textures;
5745
}
5746
5747
// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
5748
// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
5749
// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
5750
// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
5751
// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
5752
// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
5753
// - This is analogous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
5754
// which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
5755
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
5756
{
5757
ImGuiWindow* window = GetCurrentWindow();
5758
window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
5759
window->ClipRect = window->DrawList->_ClipRectStack.back();
5760
}
5761
5762
void ImGui::PopClipRect()
5763
{
5764
ImGuiWindow* window = GetCurrentWindow();
5765
window->DrawList->PopClipRect();
5766
window->ClipRect = window->DrawList->_ClipRectStack.back();
5767
}
5768
5769
static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
5770
{
5771
if ((col & IM_COL32_A_MASK) == 0)
5772
return;
5773
5774
ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport();
5775
ImRect viewport_rect = viewport->GetMainRect();
5776
5777
// Draw behind window by moving the draw command at the FRONT of the draw list
5778
{
5779
// We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows,
5780
// and draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
5781
// 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.
5782
ImDrawList* draw_list = window->RootWindow->DrawList;
5783
if (draw_list->CmdBuffer.Size == 0)
5784
draw_list->AddDrawCmd();
5785
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)
5786
draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
5787
ImDrawCmd cmd = draw_list->CmdBuffer.back();
5788
IM_ASSERT(cmd.ElemCount == 6);
5789
draw_list->CmdBuffer.pop_back();
5790
draw_list->CmdBuffer.push_front(cmd);
5791
draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
5792
draw_list->PopClipRect();
5793
}
5794
}
5795
5796
ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
5797
{
5798
ImGuiContext& g = *GImGui;
5799
ImGuiWindow* bottom_most_visible_window = parent_window;
5800
for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
5801
{
5802
ImGuiWindow* window = g.Windows[i];
5803
if (window->Flags & ImGuiWindowFlags_ChildWindow)
5804
continue;
5805
if (!IsWindowWithinBeginStackOf(window, parent_window))
5806
break;
5807
if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
5808
bottom_most_visible_window = window;
5809
}
5810
return bottom_most_visible_window;
5811
}
5812
5813
static void ImGui::RenderDimmedBackgrounds()
5814
{
5815
ImGuiContext& g = *GImGui;
5816
ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
5817
if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
5818
return;
5819
const bool dim_bg_for_modal = (modal_window != NULL);
5820
const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
5821
if (!dim_bg_for_modal && !dim_bg_for_window_list)
5822
return;
5823
5824
if (dim_bg_for_modal)
5825
{
5826
// Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
5827
ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
5828
RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
5829
}
5830
else if (dim_bg_for_window_list)
5831
{
5832
// Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
5833
RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5834
5835
// Draw border around CTRL+Tab target window
5836
ImGuiWindow* window = g.NavWindowingTargetAnim;
5837
ImGuiViewport* viewport = GetMainViewport();
5838
float distance = g.FontSize;
5839
ImRect bb = window->Rect();
5840
bb.Expand(distance);
5841
if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
5842
bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
5843
if (window->DrawList->CmdBuffer.Size == 0)
5844
window->DrawList->AddDrawCmd();
5845
window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
5846
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI
5847
window->DrawList->PopClipRect();
5848
}
5849
}
5850
5851
// 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.
5852
void ImGui::EndFrame()
5853
{
5854
ImGuiContext& g = *GImGui;
5855
IM_ASSERT(g.Initialized);
5856
5857
// Don't process EndFrame() multiple times.
5858
if (g.FrameCountEnded == g.FrameCount)
5859
return;
5860
if (!g.WithinFrameScope)
5861
{
5862
IM_ASSERT_USER_ERROR(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?");
5863
return;
5864
}
5865
5866
CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
5867
5868
// [EXPERIMENTAL] Recover from errors
5869
if (g.IO.ConfigErrorRecovery)
5870
ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
5871
ErrorCheckEndFrameSanityChecks();
5872
ErrorCheckEndFrameFinalizeErrorTooltip();
5873
5874
// Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
5875
ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
5876
if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
5877
{
5878
IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
5879
IM_ASSERT(ime_data->ViewportId == IMGUI_VIEWPORT_DEFAULT_ID); // master branch
5880
ImGuiViewport* viewport = GetMainViewport();
5881
g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);
5882
}
5883
g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0;
5884
5885
// Hide implicit/fallback "Debug" window if it hasn't been used
5886
g.WithinFrameScopeWithImplicitWindow = false;
5887
if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
5888
g.CurrentWindow->Active = false;
5889
End();
5890
5891
// Update navigation: CTRL+Tab, wrap-around requests
5892
NavEndFrame();
5893
5894
// Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
5895
if (g.DragDropActive)
5896
{
5897
bool is_delivered = g.DragDropPayload.Delivery;
5898
bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));
5899
if (is_delivered || is_elapsed)
5900
ClearDragDrop();
5901
}
5902
5903
// Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
5904
// If you want to handle source item disappearing: instead of submitting your description tooltip
5905
// in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
5906
// (e.g. end of your item loop, or before EndFrame) by reading payload data.
5907
// In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
5908
if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
5909
{
5910
g.DragDropWithinSource = true;
5911
SetTooltip("...");
5912
g.DragDropWithinSource = false;
5913
}
5914
5915
// End frame
5916
g.WithinFrameScope = false;
5917
g.FrameCountEnded = g.FrameCount;
5918
UpdateFontsEndFrame();
5919
5920
// Initiate moving window + handle left-click and right-click focus
5921
UpdateMouseMovingWindowEndFrame();
5922
5923
// Sort the window list so that all child windows are after their parent
5924
// We cannot do that on FocusWindow() because children may not exist yet
5925
g.WindowsTempSortBuffer.resize(0);
5926
g.WindowsTempSortBuffer.reserve(g.Windows.Size);
5927
for (ImGuiWindow* window : g.Windows)
5928
{
5929
if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
5930
continue;
5931
AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
5932
}
5933
5934
// 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.
5935
IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
5936
g.Windows.swap(g.WindowsTempSortBuffer);
5937
g.IO.MetricsActiveWindows = g.WindowsActiveCount;
5938
5939
UpdateTexturesEndFrame();
5940
5941
// Unlock font atlas
5942
for (ImFontAtlas* atlas : g.FontAtlases)
5943
atlas->Locked = false;
5944
5945
// Clear Input data for next frame
5946
g.IO.MousePosPrev = g.IO.MousePos;
5947
g.IO.AppFocusLost = false;
5948
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
5949
g.IO.InputQueueCharacters.resize(0);
5950
5951
CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
5952
}
5953
5954
// Prepare the data for rendering so you can call GetDrawData()
5955
// (As with anything within the ImGui:: namespace this doesn't touch your GPU or graphics API at all:
5956
// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
5957
void ImGui::Render()
5958
{
5959
ImGuiContext& g = *GImGui;
5960
IM_ASSERT(g.Initialized);
5961
5962
if (g.FrameCountEnded != g.FrameCount)
5963
EndFrame();
5964
if (g.FrameCountRendered == g.FrameCount)
5965
return;
5966
g.FrameCountRendered = g.FrameCount;
5967
5968
g.IO.MetricsRenderWindows = 0;
5969
CallContextHooks(&g, ImGuiContextHookType_RenderPre);
5970
5971
// Add background ImDrawList (for each active viewport)
5972
for (ImGuiViewportP* viewport : g.Viewports)
5973
{
5974
InitViewportDrawData(viewport);
5975
if (viewport->BgFgDrawLists[0] != NULL)
5976
AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
5977
}
5978
5979
// Draw modal/window whitening backgrounds
5980
RenderDimmedBackgrounds();
5981
5982
// Add ImDrawList to render
5983
ImGuiWindow* windows_to_render_top_most[2];
5984
windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
5985
windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
5986
for (ImGuiWindow* window : g.Windows)
5987
{
5988
IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
5989
if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
5990
AddRootWindowToDrawData(window);
5991
}
5992
for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
5993
if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
5994
AddRootWindowToDrawData(windows_to_render_top_most[n]);
5995
5996
// Draw software mouse cursor if requested by io.MouseDrawCursor flag
5997
if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
5998
RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
5999
6000
// Setup ImDrawData structures for end-user
6001
g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
6002
for (ImGuiViewportP* viewport : g.Viewports)
6003
{
6004
FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
6005
6006
// Add foreground ImDrawList (for each active viewport)
6007
if (viewport->BgFgDrawLists[1] != NULL)
6008
AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
6009
6010
// We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
6011
ImDrawData* draw_data = &viewport->DrawDataP;
6012
IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
6013
for (ImDrawList* draw_list : draw_data->CmdLists)
6014
draw_list->_PopUnusedDrawCmd();
6015
6016
g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
6017
g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
6018
}
6019
6020
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
6021
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
6022
for (ImFontAtlas* atlas : g.FontAtlases)
6023
ImFontAtlasDebugLogTextureRequests(atlas);
6024
#endif
6025
6026
CallContextHooks(&g, ImGuiContextHookType_RenderPost);
6027
}
6028
6029
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
6030
// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
6031
ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
6032
{
6033
ImGuiContext& g = *GImGui;
6034
6035
const char* text_display_end;
6036
if (hide_text_after_double_hash)
6037
text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
6038
else
6039
text_display_end = text_end;
6040
6041
ImFont* font = g.Font;
6042
const float font_size = g.FontSize;
6043
if (text == text_display_end)
6044
return ImVec2(0.0f, font_size);
6045
ImVec2 text_size = font->CalcTextSizeA(font_size, g.FontWeight, FLT_MAX, wrap_width, text, text_display_end, NULL);
6046
6047
// Round
6048
// FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
6049
// FIXME: Investigate using ceilf or e.g.
6050
// - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
6051
// - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
6052
text_size.x = IM_TRUNC(text_size.x + 0.99999f);
6053
6054
return text_size;
6055
}
6056
6057
// Find window given position, search front-to-back
6058
// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
6059
// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
6060
// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
6061
// called, aka before the next Begin(). Moving window isn't affected.
6062
// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
6063
void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
6064
{
6065
ImGuiContext& g = *GImGui;
6066
ImGuiWindow* hovered_window = NULL;
6067
ImGuiWindow* hovered_window_under_moving_window = NULL;
6068
6069
if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
6070
hovered_window = g.MovingWindow;
6071
6072
ImVec2 padding_regular = g.Style.TouchExtraPadding;
6073
ImVec2 padding_for_resize = ImMax(g.Style.TouchExtraPadding, ImVec2(g.Style.WindowBorderHoverPadding, g.Style.WindowBorderHoverPadding));
6074
for (int i = g.Windows.Size - 1; i >= 0; i--)
6075
{
6076
ImGuiWindow* window = g.Windows[i];
6077
IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6078
if (!window->WasActive || window->Hidden)
6079
continue;
6080
if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
6081
continue;
6082
6083
// Using the clipped AABB, a child window will typically be clipped by its parent (not always)
6084
ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
6085
if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
6086
continue;
6087
6088
// Support for one rectangular hole in any given window
6089
// FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
6090
if (window->HitTestHoleSize.x != 0)
6091
{
6092
ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
6093
ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
6094
if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
6095
continue;
6096
}
6097
6098
if (find_first_and_in_any_viewport)
6099
{
6100
hovered_window = window;
6101
break;
6102
}
6103
else
6104
{
6105
if (hovered_window == NULL)
6106
hovered_window = window;
6107
IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6108
if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
6109
hovered_window_under_moving_window = window;
6110
if (hovered_window && hovered_window_under_moving_window)
6111
break;
6112
}
6113
}
6114
6115
*out_hovered_window = hovered_window;
6116
if (out_hovered_window_under_moving_window != NULL)
6117
*out_hovered_window_under_moving_window = hovered_window_under_moving_window;
6118
}
6119
6120
bool ImGui::IsItemActive()
6121
{
6122
ImGuiContext& g = *GImGui;
6123
if (g.ActiveId)
6124
return g.ActiveId == g.LastItemData.ID;
6125
return false;
6126
}
6127
6128
bool ImGui::IsItemActivated()
6129
{
6130
ImGuiContext& g = *GImGui;
6131
if (g.ActiveId)
6132
if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
6133
return true;
6134
return false;
6135
}
6136
6137
bool ImGui::IsItemDeactivated()
6138
{
6139
ImGuiContext& g = *GImGui;
6140
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
6141
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
6142
return (g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount);
6143
}
6144
6145
bool ImGui::IsItemDeactivatedAfterEdit()
6146
{
6147
ImGuiContext& g = *GImGui;
6148
return IsItemDeactivated() && g.DeactivatedItemData.HasBeenEditedBefore;
6149
}
6150
6151
// == (GetItemID() == GetFocusID() && GetFocusID() != 0)
6152
bool ImGui::IsItemFocused()
6153
{
6154
ImGuiContext& g = *GImGui;
6155
return g.NavId == g.LastItemData.ID && g.NavId != 0;
6156
}
6157
6158
// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
6159
// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
6160
bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
6161
{
6162
return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
6163
}
6164
6165
bool ImGui::IsItemToggledOpen()
6166
{
6167
ImGuiContext& g = *GImGui;
6168
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
6169
}
6170
6171
// Call after a Selectable() or TreeNode() involved in multi-selection.
6172
// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
6173
// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
6174
// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
6175
// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
6176
bool ImGui::IsItemToggledSelection()
6177
{
6178
ImGuiContext& g = *GImGui;
6179
IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
6180
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
6181
}
6182
6183
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
6184
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
6185
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
6186
bool ImGui::IsAnyItemHovered()
6187
{
6188
ImGuiContext& g = *GImGui;
6189
return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
6190
}
6191
6192
bool ImGui::IsAnyItemActive()
6193
{
6194
ImGuiContext& g = *GImGui;
6195
return g.ActiveId != 0;
6196
}
6197
6198
bool ImGui::IsAnyItemFocused()
6199
{
6200
ImGuiContext& g = *GImGui;
6201
return g.NavId != 0 && g.NavCursorVisible;
6202
}
6203
6204
bool ImGui::IsItemVisible()
6205
{
6206
ImGuiContext& g = *GImGui;
6207
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
6208
}
6209
6210
bool ImGui::IsItemEdited()
6211
{
6212
ImGuiContext& g = *GImGui;
6213
return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
6214
}
6215
6216
// Allow next item to be overlapped by subsequent items.
6217
// This works by requiring HoveredId to match for two subsequent frames,
6218
// so if a following items overwrite it our interactions will naturally be disabled.
6219
void ImGui::SetNextItemAllowOverlap()
6220
{
6221
ImGuiContext& g = *GImGui;
6222
g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
6223
}
6224
6225
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6226
// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
6227
// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
6228
void ImGui::SetItemAllowOverlap()
6229
{
6230
ImGuiContext& g = *GImGui;
6231
ImGuiID id = g.LastItemData.ID;
6232
if (g.HoveredId == id)
6233
g.HoveredIdAllowOverlap = true;
6234
if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
6235
g.ActiveIdAllowOverlap = true;
6236
}
6237
#endif
6238
6239
// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
6240
// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
6241
void ImGui::SetActiveIdUsingAllKeyboardKeys()
6242
{
6243
ImGuiContext& g = *GImGui;
6244
IM_ASSERT(g.ActiveId != 0);
6245
g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
6246
g.ActiveIdUsingAllKeyboardKeys = true;
6247
NavMoveRequestCancel();
6248
}
6249
6250
ImGuiID ImGui::GetItemID()
6251
{
6252
ImGuiContext& g = *GImGui;
6253
return g.LastItemData.ID;
6254
}
6255
6256
ImVec2 ImGui::GetItemRectMin()
6257
{
6258
ImGuiContext& g = *GImGui;
6259
return g.LastItemData.Rect.Min;
6260
}
6261
6262
ImVec2 ImGui::GetItemRectMax()
6263
{
6264
ImGuiContext& g = *GImGui;
6265
return g.LastItemData.Rect.Max;
6266
}
6267
6268
ImVec2 ImGui::GetItemRectSize()
6269
{
6270
ImGuiContext& g = *GImGui;
6271
return g.LastItemData.Rect.GetSize();
6272
}
6273
6274
// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
6275
// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
6276
bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6277
{
6278
ImGuiID id = GetCurrentWindow()->GetID(str_id);
6279
return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
6280
}
6281
6282
bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6283
{
6284
return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
6285
}
6286
6287
bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6288
{
6289
ImGuiContext& g = *GImGui;
6290
ImGuiWindow* parent_window = g.CurrentWindow;
6291
IM_ASSERT(id != 0);
6292
6293
// Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
6294
const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;
6295
IM_UNUSED(ImGuiChildFlags_SupportedMask_);
6296
IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
6297
IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
6298
if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
6299
{
6300
IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6301
IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6302
}
6303
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6304
if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
6305
child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
6306
if (window_flags & ImGuiWindowFlags_NavFlattened)
6307
child_flags |= ImGuiChildFlags_NavFlattened;
6308
#endif
6309
if (child_flags & ImGuiChildFlags_AutoResizeX)
6310
child_flags &= ~ImGuiChildFlags_ResizeX;
6311
if (child_flags & ImGuiChildFlags_AutoResizeY)
6312
child_flags &= ~ImGuiChildFlags_ResizeY;
6313
6314
// Set window flags
6315
window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
6316
window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
6317
if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
6318
window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
6319
if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6320
window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
6321
6322
// Special framed style
6323
if (child_flags & ImGuiChildFlags_FrameStyle)
6324
{
6325
PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
6326
PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
6327
PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
6328
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
6329
child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;
6330
window_flags |= ImGuiWindowFlags_NoMove;
6331
}
6332
6333
// Forward size
6334
// Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
6335
// (the alternative would to store conditional flags per axis, which is possible but more code)
6336
const ImVec2 size_avail = GetContentRegionAvail();
6337
const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
6338
ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
6339
6340
// A SetNextWindowSize() call always has priority (#8020)
6341
// (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
6342
// FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
6343
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
6344
{
6345
if (g.NextWindowData.SizeVal.x > 0.0f)
6346
{
6347
size.x = g.NextWindowData.SizeVal.x;
6348
child_flags &= ~ImGuiChildFlags_ResizeX;
6349
}
6350
if (g.NextWindowData.SizeVal.y > 0.0f)
6351
{
6352
size.y = g.NextWindowData.SizeVal.y;
6353
child_flags &= ~ImGuiChildFlags_ResizeY;
6354
}
6355
}
6356
SetNextWindowSize(size);
6357
6358
// Forward child flags (we allow prior settings to merge but it'll only work for adding flags)
6359
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags)
6360
g.NextWindowData.ChildFlags |= child_flags;
6361
else
6362
g.NextWindowData.ChildFlags = child_flags;
6363
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags;
6364
6365
// 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.
6366
// 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.
6367
// e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
6368
const char* temp_window_name;
6369
/*if (name && parent_window->IDStack.back() == parent_window->ID)
6370
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
6371
else*/
6372
if (name)
6373
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
6374
else
6375
ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
6376
6377
// Set style
6378
const float backup_border_size = g.Style.ChildBorderSize;
6379
if ((child_flags & ImGuiChildFlags_Borders) == 0)
6380
g.Style.ChildBorderSize = 0.0f;
6381
6382
// Begin into window
6383
const bool ret = Begin(temp_window_name, NULL, window_flags);
6384
6385
// Restore style
6386
g.Style.ChildBorderSize = backup_border_size;
6387
if (child_flags & ImGuiChildFlags_FrameStyle)
6388
{
6389
PopStyleVar(3);
6390
PopStyleColor();
6391
}
6392
6393
ImGuiWindow* child_window = g.CurrentWindow;
6394
child_window->ChildId = id;
6395
6396
// Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
6397
// While this is not really documented/defined, it seems that the expected thing to do.
6398
if (child_window->BeginCount == 1)
6399
parent_window->DC.CursorPos = child_window->Pos;
6400
6401
// Process navigation-in immediately so NavInit can run on first frame
6402
// Can enter a child if (A) it has navigable items or (B) it can be scrolled.
6403
const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
6404
if (g.ActiveId == temp_id_for_activation)
6405
ClearActiveID();
6406
if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
6407
{
6408
FocusWindow(child_window);
6409
NavInitWindow(child_window, false);
6410
SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
6411
g.ActiveIdSource = g.NavInputSource;
6412
}
6413
return ret;
6414
}
6415
6416
void ImGui::EndChild()
6417
{
6418
ImGuiContext& g = *GImGui;
6419
ImGuiWindow* child_window = g.CurrentWindow;
6420
6421
const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
6422
IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
6423
6424
g.WithinEndChildID = child_window->ID;
6425
ImVec2 child_size = child_window->Size;
6426
End();
6427
if (child_window->BeginCount == 1)
6428
{
6429
ImGuiWindow* parent_window = g.CurrentWindow;
6430
ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
6431
ItemSize(child_size);
6432
const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
6433
if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
6434
{
6435
ItemAdd(bb, child_window->ChildId);
6436
RenderNavCursor(bb, child_window->ChildId);
6437
6438
// 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)
6439
if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
6440
RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);
6441
}
6442
else
6443
{
6444
// Not navigable into
6445
// - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
6446
// - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
6447
ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
6448
6449
// But when flattened we directly reach items, adjust active layer mask accordingly
6450
if (nav_flattened)
6451
parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
6452
}
6453
if (g.HoveredWindow == child_window)
6454
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
6455
child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags;
6456
//SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd()
6457
}
6458
else
6459
{
6460
SetLastItemDataForChildWindowItem(child_window, child_window->Rect());
6461
}
6462
6463
g.WithinEndChildID = backup_within_end_child_id;
6464
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
6465
}
6466
6467
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
6468
{
6469
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
6470
window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
6471
window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
6472
}
6473
6474
ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
6475
{
6476
ImGuiContext& g = *GImGui;
6477
return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
6478
}
6479
6480
ImGuiWindow* ImGui::FindWindowByName(const char* name)
6481
{
6482
ImGuiID id = ImHashStr(name);
6483
return FindWindowByID(id);
6484
}
6485
6486
static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6487
{
6488
window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y));
6489
if (settings->Size.x > 0 && settings->Size.y > 0)
6490
window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
6491
window->Collapsed = settings->Collapsed;
6492
}
6493
6494
static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6495
{
6496
// Initial window state with e.g. default/arbitrary window position
6497
// Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
6498
const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6499
window->Pos = main_viewport->Pos + ImVec2(60, 60);
6500
window->Size = window->SizeFull = ImVec2(0, 0);
6501
window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
6502
6503
if (settings != NULL)
6504
{
6505
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
6506
ApplyWindowSettings(window, settings);
6507
}
6508
window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
6509
6510
if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
6511
{
6512
window->AutoFitFramesX = window->AutoFitFramesY = 2;
6513
window->AutoFitOnlyGrows = false;
6514
}
6515
else
6516
{
6517
if (window->Size.x <= 0.0f)
6518
window->AutoFitFramesX = 2;
6519
if (window->Size.y <= 0.0f)
6520
window->AutoFitFramesY = 2;
6521
window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
6522
}
6523
}
6524
6525
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
6526
{
6527
// Create window the first time
6528
//IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
6529
ImGuiContext& g = *GImGui;
6530
ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
6531
window->Flags = flags;
6532
g.WindowsById.SetVoidPtr(window->ID, window);
6533
6534
ImGuiWindowSettings* settings = NULL;
6535
if (!(flags & ImGuiWindowFlags_NoSavedSettings))
6536
if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
6537
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
6538
6539
InitOrLoadWindowSettings(window, settings);
6540
6541
if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
6542
g.Windows.push_front(window); // Quite slow but rare and only once
6543
else
6544
g.Windows.push_back(window);
6545
6546
return window;
6547
}
6548
6549
static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
6550
{
6551
// We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
6552
// FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
6553
// Perhaps should tend further a neater test for this.
6554
ImGuiContext& g = *GImGui;
6555
ImVec2 size_min;
6556
if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
6557
{
6558
size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
6559
size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
6560
}
6561
else
6562
{
6563
size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
6564
size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
6565
}
6566
6567
// Reduce artifacts with very small windows
6568
ImGuiWindow* window_for_height = window;
6569
size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
6570
return size_min;
6571
}
6572
6573
static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
6574
{
6575
ImGuiContext& g = *GImGui;
6576
ImVec2 new_size = size_desired;
6577
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)
6578
{
6579
// See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
6580
ImRect cr = g.NextWindowData.SizeConstraintRect;
6581
new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
6582
new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
6583
if (g.NextWindowData.SizeCallback)
6584
{
6585
ImGuiSizeCallbackData data;
6586
data.UserData = g.NextWindowData.SizeCallbackUserData;
6587
data.Pos = window->Pos;
6588
data.CurrentSize = window->SizeFull;
6589
data.DesiredSize = new_size;
6590
g.NextWindowData.SizeCallback(&data);
6591
new_size = data.DesiredSize;
6592
}
6593
new_size.x = IM_TRUNC(new_size.x);
6594
new_size.y = IM_TRUNC(new_size.y);
6595
}
6596
6597
// Minimum size
6598
ImVec2 size_min = CalcWindowMinSize(window);
6599
return ImMax(new_size, size_min);
6600
}
6601
6602
static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
6603
{
6604
bool preserve_old_content_sizes = false;
6605
if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6606
preserve_old_content_sizes = true;
6607
else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
6608
preserve_old_content_sizes = true;
6609
if (preserve_old_content_sizes)
6610
{
6611
*content_size_current = window->ContentSize;
6612
*content_size_ideal = window->ContentSizeIdeal;
6613
return;
6614
}
6615
6616
content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
6617
content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
6618
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);
6619
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);
6620
}
6621
6622
static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
6623
{
6624
ImGuiContext& g = *GImGui;
6625
ImGuiStyle& style = g.Style;
6626
const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
6627
const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
6628
ImVec2 size_pad = window->WindowPadding * 2.0f;
6629
ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
6630
6631
// Determine maximum window size
6632
// Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction
6633
ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6634
6635
if (window->Flags & ImGuiWindowFlags_Tooltip)
6636
{
6637
// Tooltip always resize (up to maximum size)
6638
return ImMin(size_desired, size_max);
6639
}
6640
else
6641
{
6642
ImVec2 size_min = CalcWindowMinSize(window);
6643
ImVec2 size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max);
6644
6645
// FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
6646
// we may need to compute/store three variants of size_auto_fit, for x/y/xy.
6647
// Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
6648
if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & (ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeY)))
6649
size_auto_fit.y = window->SizeFull.y;
6650
else if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && !(window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_AutoResizeX)))
6651
size_auto_fit.x = window->SizeFull.x;
6652
6653
// When the window cannot fit all contents (either because of constraints, either because screen is too small),
6654
// we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
6655
ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6656
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);
6657
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);
6658
if (will_have_scrollbar_x)
6659
size_auto_fit.y += style.ScrollbarSize;
6660
if (will_have_scrollbar_y)
6661
size_auto_fit.x += style.ScrollbarSize;
6662
return size_auto_fit;
6663
}
6664
}
6665
6666
ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
6667
{
6668
ImVec2 size_contents_current;
6669
ImVec2 size_contents_ideal;
6670
CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
6671
ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
6672
ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6673
return size_final;
6674
}
6675
6676
static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
6677
{
6678
if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
6679
return ImGuiCol_PopupBg;
6680
if (window->Flags & ImGuiWindowFlags_ChildWindow)
6681
return ImGuiCol_ChildBg;
6682
return ImGuiCol_WindowBg;
6683
}
6684
6685
static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
6686
{
6687
ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
6688
ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
6689
ImVec2 size_expected = pos_max - pos_min;
6690
ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
6691
*out_pos = pos_min;
6692
if (corner_norm.x == 0.0f)
6693
out_pos->x -= (size_constrained.x - size_expected.x);
6694
if (corner_norm.y == 0.0f)
6695
out_pos->y -= (size_constrained.y - size_expected.y);
6696
*out_size = size_constrained;
6697
}
6698
6699
// Data for resizing from resize grip / corner
6700
struct ImGuiResizeGripDef
6701
{
6702
ImVec2 CornerPosN;
6703
ImVec2 InnerDir;
6704
int AngleMin12, AngleMax12;
6705
};
6706
static const ImGuiResizeGripDef resize_grip_def[4] =
6707
{
6708
{ ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
6709
{ ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
6710
{ ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
6711
{ ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
6712
};
6713
6714
// Data for resizing from borders
6715
struct ImGuiResizeBorderDef
6716
{
6717
ImVec2 InnerDir; // Normal toward inside
6718
ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left)
6719
float OuterAngle; // Angle toward outside
6720
};
6721
static const ImGuiResizeBorderDef resize_border_def[4] =
6722
{
6723
{ ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
6724
{ ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
6725
{ ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
6726
{ ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down
6727
};
6728
6729
static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
6730
{
6731
ImRect rect = window->Rect();
6732
if (thickness == 0.0f)
6733
rect.Max -= ImVec2(1, 1);
6734
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); }
6735
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); }
6736
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); }
6737
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); }
6738
IM_ASSERT(0);
6739
return ImRect();
6740
}
6741
6742
// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
6743
ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
6744
{
6745
IM_ASSERT(n >= 0 && n < 4);
6746
ImGuiID id = window->ID;
6747
id = ImHashStr("#RESIZE", 0, id);
6748
id = ImHashData(&n, sizeof(int), id);
6749
return id;
6750
}
6751
6752
// Borders (Left, Right, Up, Down)
6753
ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
6754
{
6755
IM_ASSERT(dir >= 0 && dir < 4);
6756
int n = (int)dir + 4;
6757
ImGuiID id = window->ID;
6758
id = ImHashStr("#RESIZE", 0, id);
6759
id = ImHashData(&n, sizeof(int), id);
6760
return id;
6761
}
6762
6763
// Handle resize for: Resize Grips, Borders, Gamepad
6764
// Return true when using auto-fit (double-click on resize grip)
6765
static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
6766
{
6767
ImGuiContext& g = *GImGui;
6768
ImGuiWindowFlags flags = window->Flags;
6769
6770
if ((flags & ImGuiWindowFlags_NoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6771
return false;
6772
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && (window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6773
return false;
6774
if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
6775
return false;
6776
6777
int ret_auto_fit_mask = 0x00;
6778
const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6779
const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
6780
const float grip_hover_outer_size = g.WindowsBorderHoverPadding;
6781
6782
ImRect clamp_rect = visibility_rect;
6783
const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
6784
if (window_move_from_title_bar)
6785
clamp_rect.Min.y -= window->TitleBarHeight;
6786
6787
ImVec2 pos_target(FLT_MAX, FLT_MAX);
6788
ImVec2 size_target(FLT_MAX, FLT_MAX);
6789
6790
// Resize grips and borders are on layer 1
6791
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6792
6793
// Manual resize grips
6794
PushID("#RESIZE");
6795
for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6796
{
6797
const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
6798
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
6799
6800
// Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6801
bool hovered, held;
6802
ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
6803
if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
6804
if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
6805
ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
6806
ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
6807
ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6808
//GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
6809
if (hovered || held)
6810
SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
6811
6812
if (held && g.IO.MouseDoubleClicked[0])
6813
{
6814
// Auto-fit when double-clicking
6815
size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6816
ret_auto_fit_mask = 0x03; // Both axes
6817
ClearActiveID();
6818
}
6819
else if (held)
6820
{
6821
// Resize from any of the four corners
6822
// We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6823
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);
6824
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);
6825
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
6826
corner_target = ImClamp(corner_target, clamp_min, clamp_max);
6827
CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
6828
}
6829
6830
// Only lower-left grip is visible before hovering/activating
6831
if (resize_grip_n == 0 || held || hovered)
6832
resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
6833
}
6834
6835
int resize_border_mask = 0x00;
6836
if (window->Flags & ImGuiWindowFlags_ChildWindow)
6837
resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
6838
else
6839
resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
6840
for (int border_n = 0; border_n < 4; border_n++)
6841
{
6842
if ((resize_border_mask & (1 << border_n)) == 0)
6843
continue;
6844
const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6845
const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
6846
6847
bool hovered, held;
6848
ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, g.WindowsBorderHoverPadding);
6849
ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
6850
ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
6851
ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6852
//GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
6853
if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
6854
hovered = false;
6855
if (hovered || held)
6856
SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
6857
if (held && g.IO.MouseDoubleClicked[0])
6858
{
6859
// Double-clicking bottom or right border auto-fit on this axis
6860
// FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
6861
// FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
6862
if (border_n == 1 || border_n == 3) // Right and bottom border
6863
{
6864
size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
6865
ret_auto_fit_mask |= (1 << axis);
6866
hovered = held = false; // So border doesn't show highlighted at new position
6867
}
6868
ClearActiveID();
6869
}
6870
else if (held)
6871
{
6872
// Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
6873
// 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.
6874
// FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
6875
const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false));
6876
if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
6877
{
6878
g.WindowResizeBorderExpectedRect = border_rect;
6879
g.WindowResizeRelativeMode = false;
6880
}
6881
if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
6882
g.WindowResizeRelativeMode = true;
6883
6884
const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
6885
const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
6886
const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + g.WindowsBorderHoverPadding; // Match ButtonBehavior() padding above.
6887
6888
// Use absolute mode position
6889
ImVec2 border_target = window->Pos;
6890
border_target[axis] = border_target_abs_mode_for_axis;
6891
6892
// Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
6893
bool ignore_resize = false;
6894
if (g.WindowResizeRelativeMode)
6895
{
6896
//GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
6897
border_target[axis] = border_target_rel_mode_for_axis;
6898
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))
6899
ignore_resize = true;
6900
}
6901
6902
// Clamp, apply
6903
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);
6904
ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
6905
border_target = ImClamp(border_target, clamp_min, clamp_max);
6906
if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
6907
{
6908
ImGuiWindow* parent_window = window->ParentWindow;
6909
ImGuiWindowFlags parent_flags = parent_window->Flags;
6910
ImRect border_limit_rect = parent_window->InnerRect;
6911
border_limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));
6912
if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)))
6913
border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x);
6914
if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar))
6915
border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y);
6916
}
6917
if (!ignore_resize)
6918
CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
6919
}
6920
if (hovered)
6921
*border_hovered = border_n;
6922
if (held)
6923
*border_held = border_n;
6924
}
6925
PopID();
6926
6927
// Restore nav layer
6928
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6929
6930
// Navigation resize (keyboard/gamepad)
6931
// FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
6932
// Not even sure the callback works here.
6933
if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
6934
{
6935
ImVec2 nav_resize_dir;
6936
if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
6937
nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
6938
if (g.NavInputSource == ImGuiInputSource_Gamepad)
6939
nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
6940
if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
6941
{
6942
const float NAV_RESIZE_SPEED = 600.0f;
6943
const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
6944
g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
6945
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
6946
g.NavWindowingToggleLayer = false;
6947
g.NavHighlightItemUnderNav = true;
6948
resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
6949
ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
6950
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
6951
{
6952
// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
6953
size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
6954
g.NavWindowingAccumDeltaSize -= accum_floored;
6955
}
6956
}
6957
}
6958
6959
// Apply back modified position/size to window
6960
const ImVec2 curr_pos = window->Pos;
6961
const ImVec2 curr_size = window->SizeFull;
6962
if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
6963
window->Size.x = window->SizeFull.x = size_target.x;
6964
if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
6965
window->Size.y = window->SizeFull.y = size_target.y;
6966
if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
6967
window->Pos.x = ImTrunc(pos_target.x);
6968
if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
6969
window->Pos.y = ImTrunc(pos_target.y);
6970
if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y)
6971
MarkIniSettingsDirty(window);
6972
6973
// Recalculate next expected border expected coordinates
6974
if (*border_held != -1)
6975
g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, g.WindowsBorderHoverPadding);
6976
6977
return ret_auto_fit_mask;
6978
}
6979
6980
static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
6981
{
6982
ImGuiContext& g = *GImGui;
6983
ImVec2 size_for_clamping = window->Size;
6984
if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6985
size_for_clamping.y = window->TitleBarHeight;
6986
window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
6987
}
6988
6989
static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
6990
{
6991
const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6992
const float rounding = window->WindowRounding;
6993
const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
6994
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);
6995
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);
6996
window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
6997
}
6998
6999
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
7000
{
7001
ImGuiContext& g = *GImGui;
7002
const float border_size = window->WindowBorderSize;
7003
const ImU32 border_col = GetColorU32(ImGuiCol_Border);
7004
if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
7005
window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
7006
else if (border_size > 0.0f)
7007
{
7008
if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
7009
RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
7010
if (window->ChildFlags & ImGuiChildFlags_ResizeY)
7011
RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
7012
}
7013
if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
7014
{
7015
const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
7016
const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
7017
RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
7018
}
7019
if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7020
{
7021
float y = window->Pos.y + window->TitleBarHeight - 1;
7022
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);
7023
}
7024
}
7025
7026
// Draw background and borders
7027
// Draw and handle scrollbars
7028
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)
7029
{
7030
ImGuiContext& g = *GImGui;
7031
ImGuiStyle& style = g.Style;
7032
ImGuiWindowFlags flags = window->Flags;
7033
7034
// Ensure that Scrollbar() doesn't read last frame's SkipItems
7035
IM_ASSERT(window->BeginCount == 0);
7036
window->SkipItems = false;
7037
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7038
7039
// Draw window + handle manual resize
7040
// As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
7041
const float window_rounding = window->WindowRounding;
7042
const float window_border_size = window->WindowBorderSize;
7043
if (window->Collapsed)
7044
{
7045
// Title bar only
7046
const float backup_border_size = style.FrameBorderSize;
7047
g.Style.FrameBorderSize = window->WindowBorderSize;
7048
ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
7049
RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
7050
g.Style.FrameBorderSize = backup_border_size;
7051
}
7052
else
7053
{
7054
// Window background
7055
if (!(flags & ImGuiWindowFlags_NoBackground))
7056
{
7057
ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
7058
bool override_alpha = false;
7059
float alpha = 1.0f;
7060
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasBgAlpha)
7061
{
7062
alpha = g.NextWindowData.BgAlphaVal;
7063
override_alpha = true;
7064
}
7065
if (override_alpha)
7066
bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
7067
window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
7068
}
7069
7070
// Title bar
7071
if (!(flags & ImGuiWindowFlags_NoTitleBar))
7072
{
7073
ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
7074
window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
7075
}
7076
7077
// Menu bar
7078
if (flags & ImGuiWindowFlags_MenuBar)
7079
{
7080
ImRect menu_bar_rect = window->MenuBarRect();
7081
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.
7082
window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
7083
if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
7084
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);
7085
}
7086
7087
// Scrollbars
7088
if (window->ScrollbarX)
7089
Scrollbar(ImGuiAxis_X);
7090
if (window->ScrollbarY)
7091
Scrollbar(ImGuiAxis_Y);
7092
7093
// Render resize grips (after their input handling so we don't have a frame of latency)
7094
if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
7095
{
7096
for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
7097
{
7098
const ImU32 col = resize_grip_col[resize_grip_n];
7099
if ((col & IM_COL32_A_MASK) == 0)
7100
continue;
7101
const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
7102
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
7103
const float border_inner = IM_ROUND(window_border_size * 0.5f);
7104
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner)));
7105
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size)));
7106
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);
7107
window->DrawList->PathFillConvex(col);
7108
}
7109
}
7110
7111
// Borders
7112
if (handle_borders_and_resize_grips)
7113
RenderWindowOuterBorders(window);
7114
}
7115
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7116
}
7117
7118
// Render title text, collapse button, close button
7119
void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
7120
{
7121
ImGuiContext& g = *GImGui;
7122
ImGuiStyle& style = g.Style;
7123
ImGuiWindowFlags flags = window->Flags;
7124
7125
const bool has_close_button = (p_open != NULL);
7126
const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
7127
7128
// Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
7129
// 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?
7130
const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
7131
g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
7132
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7133
7134
// Layout buttons
7135
// FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
7136
float pad_l = style.FramePadding.x;
7137
float pad_r = style.FramePadding.x;
7138
float button_sz = g.FontSize;
7139
ImVec2 close_button_pos;
7140
ImVec2 collapse_button_pos;
7141
if (has_close_button)
7142
{
7143
close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7144
pad_r += button_sz + style.ItemInnerSpacing.x;
7145
}
7146
if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
7147
{
7148
collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7149
pad_r += button_sz + style.ItemInnerSpacing.x;
7150
}
7151
if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
7152
{
7153
collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
7154
pad_l += button_sz + style.ItemInnerSpacing.x;
7155
}
7156
7157
// Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
7158
if (has_collapse_button)
7159
if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
7160
window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
7161
7162
// Close button
7163
if (has_close_button)
7164
{
7165
g.CurrentItemFlags |= ImGuiItemFlags_NoFocus;
7166
if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
7167
*p_open = false;
7168
g.CurrentItemFlags &= ~ImGuiItemFlags_NoFocus;
7169
}
7170
7171
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7172
g.CurrentItemFlags = item_flags_backup;
7173
7174
// Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
7175
// FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
7176
const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
7177
const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
7178
7179
// As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
7180
// while uncentered title text will still reach edges correctly.
7181
if (pad_l > style.FramePadding.x)
7182
pad_l += g.Style.ItemInnerSpacing.x;
7183
if (pad_r > style.FramePadding.x)
7184
pad_r += g.Style.ItemInnerSpacing.x;
7185
if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
7186
{
7187
float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
7188
float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
7189
pad_l = ImMax(pad_l, pad_extend * centerness);
7190
pad_r = ImMax(pad_r, pad_extend * centerness);
7191
}
7192
7193
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);
7194
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);
7195
if (flags & ImGuiWindowFlags_UnsavedDocument)
7196
{
7197
ImVec2 marker_pos;
7198
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);
7199
marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
7200
if (marker_pos.x > layout_r.Min.x)
7201
{
7202
RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
7203
clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
7204
}
7205
}
7206
//if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7207
//if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7208
RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
7209
}
7210
7211
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
7212
{
7213
window->ParentWindow = parent_window;
7214
window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
7215
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
7216
window->RootWindow = parent_window->RootWindow;
7217
if (parent_window && (flags & ImGuiWindowFlags_Popup))
7218
window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
7219
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
7220
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
7221
while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
7222
{
7223
IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
7224
window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
7225
}
7226
}
7227
7228
// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
7229
// This is designed as a toy/test-bed for
7230
void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
7231
{
7232
ImGuiContext& g = *GImGui;
7233
window->SkipRefresh = false;
7234
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
7235
return;
7236
if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
7237
{
7238
// FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
7239
if (window->Appearing) // If currently appearing
7240
return;
7241
if (window->Hidden) // If was hidden (previous frame)
7242
return;
7243
if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)
7244
if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window))
7245
return;
7246
if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)
7247
if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window))
7248
return;
7249
window->DrawList = NULL;
7250
window->SkipRefresh = true;
7251
}
7252
}
7253
7254
static void SetWindowActiveForSkipRefresh(ImGuiWindow* window)
7255
{
7256
window->Active = true;
7257
for (ImGuiWindow* child : window->DC.ChildWindows)
7258
if (!child->Hidden)
7259
{
7260
child->Active = child->SkipRefresh = true;
7261
SetWindowActiveForSkipRefresh(child);
7262
}
7263
}
7264
7265
// Push a new Dear ImGui window to add widgets to.
7266
// - 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.
7267
// - Begin/End can be called multiple times during the frame with the same window name to append content.
7268
// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
7269
// 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.
7270
// - 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.
7271
// - 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.
7272
bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
7273
{
7274
ImGuiContext& g = *GImGui;
7275
const ImGuiStyle& style = g.Style;
7276
IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
7277
IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
7278
IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
7279
7280
// Find or create
7281
ImGuiWindow* window = FindWindowByName(name);
7282
const bool window_just_created = (window == NULL);
7283
if (window_just_created)
7284
window = CreateNewWindow(name, flags);
7285
7286
// [DEBUG] Debug break requested by user
7287
if (g.DebugBreakInWindow == window->ID)
7288
IM_DEBUG_BREAK();
7289
7290
// Automatically disable manual moving/resizing when NoInputs is set
7291
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
7292
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
7293
7294
const int current_frame = g.FrameCount;
7295
const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
7296
window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
7297
7298
// Update the Appearing flag
7299
bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
7300
if (flags & ImGuiWindowFlags_Popup)
7301
{
7302
ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7303
window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
7304
window_just_activated_by_user |= (window != popup_ref.Window);
7305
}
7306
window->Appearing = window_just_activated_by_user;
7307
if (window->Appearing)
7308
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
7309
7310
// Update Flags, LastFrameActive, BeginOrderXXX fields
7311
if (first_begin_of_the_frame)
7312
{
7313
UpdateWindowInFocusOrderList(window, window_just_created, flags);
7314
window->Flags = (ImGuiWindowFlags)flags;
7315
window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
7316
window->LastFrameActive = current_frame;
7317
window->LastTimeActive = (float)g.Time;
7318
window->BeginOrderWithinParent = 0;
7319
window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
7320
}
7321
else
7322
{
7323
flags = window->Flags;
7324
}
7325
7326
// 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
7327
ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
7328
ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
7329
IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
7330
7331
// We allow window memory to be compacted so recreate the base stack when needed.
7332
if (window->IDStack.Size == 0)
7333
window->IDStack.push_back(window->ID);
7334
7335
// Add to stack
7336
g.CurrentWindow = window;
7337
g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
7338
ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
7339
window_stack_data.Window = window;
7340
window_stack_data.ParentLastItemDataBackup = g.LastItemData;
7341
window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
7342
window_stack_data.DisabledOverrideReenableAlphaBackup = 0.0f;
7343
ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
7344
g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
7345
if (flags & ImGuiWindowFlags_ChildMenu)
7346
g.BeginMenuDepth++;
7347
7348
// Update ->RootWindow and others pointers (before any possible call to FocusWindow)
7349
if (first_begin_of_the_frame)
7350
{
7351
UpdateWindowParentAndRootLinks(window, flags, parent_window);
7352
window->ParentWindowInBeginStack = parent_window_in_stack;
7353
7354
// There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
7355
// e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
7356
window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL;
7357
7358
// Inherent SetWindowFontScale() from parent until we fix this system...
7359
window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f;
7360
}
7361
7362
// Add to focus scope stack
7363
PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
7364
window->NavRootFocusScopeId = g.CurrentFocusScopeId;
7365
7366
// Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
7367
if (flags & ImGuiWindowFlags_Popup)
7368
{
7369
ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7370
popup_ref.Window = window;
7371
popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
7372
g.BeginPopupStack.push_back(popup_ref);
7373
window->PopupId = popup_ref.PopupId;
7374
}
7375
7376
// Process SetNextWindow***() calls
7377
// (FIXME: Consider splitting the HasXXX flags into X/Y components
7378
bool window_pos_set_by_api = false;
7379
bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
7380
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos)
7381
{
7382
window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
7383
if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
7384
{
7385
// May be processed on the next frame if this is our first frame and we are measuring size
7386
// FIXME: Look into removing the branch so everything can go through this same code path for consistency.
7387
window->SetWindowPosVal = g.NextWindowData.PosVal;
7388
window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
7389
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7390
}
7391
else
7392
{
7393
SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
7394
}
7395
}
7396
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize)
7397
{
7398
window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
7399
window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
7400
if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
7401
g.NextWindowData.SizeVal.x = window->SizeFull.x;
7402
if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
7403
g.NextWindowData.SizeVal.y = window->SizeFull.y;
7404
SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
7405
}
7406
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll)
7407
{
7408
if (g.NextWindowData.ScrollVal.x >= 0.0f)
7409
{
7410
window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
7411
window->ScrollTargetCenterRatio.x = 0.0f;
7412
}
7413
if (g.NextWindowData.ScrollVal.y >= 0.0f)
7414
{
7415
window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
7416
window->ScrollTargetCenterRatio.y = 0.0f;
7417
}
7418
}
7419
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasContentSize)
7420
window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
7421
else if (first_begin_of_the_frame)
7422
window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
7423
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasCollapsed)
7424
SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
7425
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasFocus)
7426
FocusWindow(window);
7427
if (window->Appearing)
7428
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
7429
7430
// [EXPERIMENTAL] Skip Refresh mode
7431
UpdateWindowSkipRefresh(window);
7432
7433
// Nested root windows (typically tooltips) override disabled state
7434
if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
7435
BeginDisabledOverrideReenable();
7436
7437
// We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
7438
g.CurrentWindow = NULL;
7439
7440
// When reusing window again multiple times a frame, just append content (don't need to setup again)
7441
if (first_begin_of_the_frame && !window->SkipRefresh)
7442
{
7443
// Initialize
7444
const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
7445
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
7446
window->Active = true;
7447
window->HasCloseButton = (p_open != NULL);
7448
window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
7449
window->IDStack.resize(1);
7450
window->DrawList->_ResetForNewFrame();
7451
window->DC.CurrentTableIdx = -1;
7452
7453
// Restore buffer capacity when woken from a compacted state, to avoid
7454
if (window->MemoryCompacted)
7455
GcAwakeTransientWindowBuffers(window);
7456
7457
// Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
7458
// 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.
7459
bool window_title_visible_elsewhere = false;
7460
if (g.NavWindowingListWindow != NULL && (flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
7461
window_title_visible_elsewhere = true;
7462
if (flags & ImGuiWindowFlags_ChildMenu)
7463
window_title_visible_elsewhere = true;
7464
if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
7465
{
7466
size_t buf_len = (size_t)window->NameBufLen;
7467
window->Name = ImStrdupcpy(window->Name, &buf_len, name);
7468
window->NameBufLen = (int)buf_len;
7469
}
7470
7471
// UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
7472
7473
// Update contents size from last frame for auto-fitting (or use explicit size)
7474
CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
7475
if (window->HiddenFramesCanSkipItems > 0)
7476
window->HiddenFramesCanSkipItems--;
7477
if (window->HiddenFramesCannotSkipItems > 0)
7478
window->HiddenFramesCannotSkipItems--;
7479
if (window->HiddenFramesForRenderOnly > 0)
7480
window->HiddenFramesForRenderOnly--;
7481
7482
// Hide new windows for one frame until they calculate their size
7483
if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
7484
window->HiddenFramesCannotSkipItems = 1;
7485
7486
// Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
7487
// We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
7488
if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
7489
{
7490
window->HiddenFramesCannotSkipItems = 1;
7491
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
7492
{
7493
if (!window_size_x_set_by_api)
7494
window->Size.x = window->SizeFull.x = 0.f;
7495
if (!window_size_y_set_by_api)
7496
window->Size.y = window->SizeFull.y = 0.f;
7497
window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
7498
}
7499
}
7500
7501
// SELECT VIEWPORT
7502
// FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
7503
7504
ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
7505
SetWindowViewport(window, viewport);
7506
SetCurrentWindow(window);
7507
7508
// LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
7509
7510
if (flags & ImGuiWindowFlags_ChildWindow)
7511
window->WindowBorderSize = style.ChildBorderSize;
7512
else
7513
window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
7514
window->WindowPadding = style.WindowPadding;
7515
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
7516
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
7517
7518
// Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
7519
window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
7520
window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
7521
window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
7522
window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
7523
window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window.
7524
window->ScrollStepSize = style.ScrollStepSize;
7525
7526
// Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
7527
// Those flags will be altered further down in the function depending on more conditions.
7528
bool use_current_size_for_scrollbar_x = window_just_created;
7529
bool use_current_size_for_scrollbar_y = window_just_created;
7530
if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
7531
use_current_size_for_scrollbar_x = true;
7532
if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
7533
use_current_size_for_scrollbar_y = true;
7534
7535
// Collapse window by double-clicking on title bar
7536
// 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
7537
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
7538
{
7539
// We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
7540
// so verify that we don't have items over the title bar.
7541
ImRect title_bar_rect = window->TitleBarRect();
7542
if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
7543
if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
7544
window->WantCollapseToggle = true;
7545
if (window->WantCollapseToggle)
7546
{
7547
window->Collapsed = !window->Collapsed;
7548
if (!window->Collapsed)
7549
use_current_size_for_scrollbar_y = true;
7550
MarkIniSettingsDirty(window);
7551
}
7552
}
7553
else
7554
{
7555
window->Collapsed = false;
7556
}
7557
window->WantCollapseToggle = false;
7558
7559
// SIZE
7560
7561
// Outer Decoration Sizes
7562
// (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
7563
const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
7564
window->DecoOuterSizeX1 = 0.0f;
7565
window->DecoOuterSizeX2 = 0.0f;
7566
window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
7567
window->DecoOuterSizeY2 = 0.0f;
7568
window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
7569
7570
// Calculate auto-fit size, handle automatic resize
7571
const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
7572
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
7573
{
7574
// Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
7575
if (!window_size_x_set_by_api)
7576
{
7577
window->SizeFull.x = size_auto_fit.x;
7578
use_current_size_for_scrollbar_x = true;
7579
}
7580
if (!window_size_y_set_by_api)
7581
{
7582
window->SizeFull.y = size_auto_fit.y;
7583
use_current_size_for_scrollbar_y = true;
7584
}
7585
}
7586
else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7587
{
7588
// Auto-fit may only grow window during the first few frames
7589
// We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
7590
if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
7591
{
7592
window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
7593
use_current_size_for_scrollbar_x = true;
7594
}
7595
if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
7596
{
7597
window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
7598
use_current_size_for_scrollbar_y = true;
7599
}
7600
if (!window->Collapsed)
7601
MarkIniSettingsDirty(window);
7602
}
7603
7604
// Apply minimum/maximum window size constraints and final size
7605
window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
7606
window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
7607
7608
// POSITION
7609
7610
// Popup latch its initial position, will position itself when it appears next frame
7611
if (window_just_activated_by_user)
7612
{
7613
window->AutoPosLastDirection = ImGuiDir_None;
7614
if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
7615
window->Pos = g.BeginPopupStack.back().OpenPopupPos;
7616
}
7617
7618
// Position child window
7619
if (flags & ImGuiWindowFlags_ChildWindow)
7620
{
7621
IM_ASSERT(parent_window && parent_window->Active);
7622
window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
7623
parent_window->DC.ChildWindows.push_back(window);
7624
if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
7625
window->Pos = parent_window->DC.CursorPos;
7626
}
7627
7628
const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
7629
if (window_pos_with_pivot)
7630
SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
7631
else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
7632
window->Pos = FindBestWindowPosForPopup(window);
7633
else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
7634
window->Pos = FindBestWindowPosForPopup(window);
7635
else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
7636
window->Pos = FindBestWindowPosForPopup(window);
7637
7638
// Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
7639
// When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
7640
ImRect viewport_rect(viewport->GetMainRect());
7641
ImRect viewport_work_rect(viewport->GetWorkRect());
7642
ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
7643
ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
7644
7645
// Clamp position/size so window stays visible within its viewport or monitor
7646
// Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
7647
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
7648
if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
7649
ClampWindowPos(window, visibility_rect);
7650
window->Pos = ImTrunc(window->Pos);
7651
7652
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
7653
// Large values tend to lead to variety of artifacts and are not recommended.
7654
window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
7655
7656
// For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
7657
//if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7658
// window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
7659
7660
// Apply window focus (new and reactivated windows are moved to front)
7661
bool want_focus = false;
7662
if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
7663
{
7664
if (flags & ImGuiWindowFlags_Popup)
7665
want_focus = true;
7666
else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
7667
want_focus = true;
7668
}
7669
7670
// [Test Engine] Register whole window in the item system (before submitting further decorations)
7671
#ifdef IMGUI_ENABLE_TEST_ENGINE
7672
if (g.TestEngineHookItems)
7673
{
7674
IM_ASSERT(window->IDStack.Size == 1);
7675
window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
7676
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7677
IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
7678
IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
7679
window->IDStack.Size = 1;
7680
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7681
7682
}
7683
#endif
7684
7685
// Handle manual resize: Resize Grips, Borders, Gamepad
7686
int border_hovered = -1, border_held = -1;
7687
ImU32 resize_grip_col[4] = {};
7688
const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
7689
const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
7690
if (!window->Collapsed)
7691
if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
7692
{
7693
if (auto_fit_mask & (1 << ImGuiAxis_X))
7694
use_current_size_for_scrollbar_x = true;
7695
if (auto_fit_mask & (1 << ImGuiAxis_Y))
7696
use_current_size_for_scrollbar_y = true;
7697
}
7698
window->ResizeBorderHovered = (signed char)border_hovered;
7699
window->ResizeBorderHeld = (signed char)border_held;
7700
7701
// SCROLLBAR VISIBILITY
7702
7703
// Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
7704
if (!window->Collapsed)
7705
{
7706
// When reading the current size we need to read it after size constraints have been applied.
7707
// Intentionally use previous frame values for InnerRect and ScrollbarSizes.
7708
// And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
7709
ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
7710
ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
7711
ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
7712
float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
7713
float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
7714
bool scrollbar_x_prev = window->ScrollbarX;
7715
//bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
7716
window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
7717
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));
7718
7719
// Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488)
7720
// (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.
7721
// 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)
7722
window->ScrollbarXStabilizeToggledHistory <<= 1;
7723
window->ScrollbarXStabilizeToggledHistory |= (scrollbar_x_prev != window->ScrollbarX) ? 0x01 : 0x00;
7724
const bool scrollbar_x_stabilize = (window->ScrollbarXStabilizeToggledHistory != 0) && ImCountSetBits(window->ScrollbarXStabilizeToggledHistory) >= 4; // 4 == half of bits in our U8 history.
7725
if (scrollbar_x_stabilize)
7726
window->ScrollbarX = true;
7727
//if (scrollbar_x_stabilize && !window->ScrollbarXStabilizeEnabled)
7728
// IMGUI_DEBUG_LOG("[scroll] Stabilize ScrollbarX for Window '%s'\n", window->Name);
7729
window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize;
7730
7731
if (window->ScrollbarX && !window->ScrollbarY)
7732
window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
7733
window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
7734
7735
// Amend the partially filled window->DecorationXXX values.
7736
window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
7737
window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
7738
}
7739
7740
// UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
7741
// Update various regions. Variables they depend on should be set above in this function.
7742
// We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
7743
7744
// Outer rectangle
7745
// Not affected by window border size. Used by:
7746
// - FindHoveredWindow() (w/ extra padding when border resize is enabled)
7747
// - Begin() initial clipping rect for drawing window background and borders.
7748
// - Begin() clipping whole child
7749
const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
7750
const ImRect outer_rect = window->Rect();
7751
const ImRect title_bar_rect = window->TitleBarRect();
7752
window->OuterRectClipped = outer_rect;
7753
window->OuterRectClipped.ClipWith(host_rect);
7754
7755
// Inner rectangle
7756
// Not affected by window border size. Used by:
7757
// - InnerClipRect
7758
// - ScrollToRectEx()
7759
// - NavUpdatePageUpPageDown()
7760
// - Scrollbar()
7761
window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
7762
window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
7763
window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
7764
window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
7765
7766
// Inner clipping rectangle.
7767
// - Extend a outside of normal work region up to borders.
7768
// - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
7769
// - It also makes clipped items be more noticeable.
7770
// - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
7771
// - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
7772
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
7773
// Affected by window/frame border size. Used by:
7774
// - Begin() initial clip rect
7775
float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
7776
7777
// Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
7778
// This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,
7779
// 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).
7780
window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);
7781
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);
7782
window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);
7783
window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);
7784
window->InnerClipRect.ClipWithFull(host_rect);
7785
7786
// SCROLLING
7787
7788
// Lock down maximum scrolling
7789
// The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
7790
// for right/bottom aligned items without creating a scrollbar.
7791
window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
7792
window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
7793
7794
// Apply scrolling
7795
window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7796
window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
7797
window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
7798
7799
// DRAWING
7800
7801
// Setup draw list and outer clipping rectangle
7802
IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
7803
window->DrawList->PushTexture(g.Font->ContainerAtlas->TexRef);
7804
PushClipRect(host_rect.Min, host_rect.Max, false);
7805
7806
// Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
7807
// When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
7808
// 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)
7809
{
7810
bool render_decorations_in_parent = false;
7811
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
7812
{
7813
// - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
7814
// - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
7815
ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
7816
bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
7817
bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
7818
if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
7819
render_decorations_in_parent = true;
7820
}
7821
if (render_decorations_in_parent)
7822
window->DrawList = parent_window->DrawList;
7823
7824
// Handle title bar, scrollbar, resize grips and resize borders
7825
const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
7826
const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
7827
const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch.
7828
RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
7829
7830
if (render_decorations_in_parent)
7831
window->DrawList = &window->DrawListInst;
7832
}
7833
7834
// UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
7835
7836
// Work rectangle.
7837
// Affected by window padding and border size. Used by:
7838
// - Columns() for right-most edge
7839
// - TreeNode(), CollapsingHeader() for right-most edge
7840
// - BeginTabBar() for right-most edge
7841
const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
7842
const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
7843
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)));
7844
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)));
7845
window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
7846
window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
7847
window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
7848
window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
7849
window->ParentWorkRect = window->WorkRect;
7850
7851
// [LEGACY] Content Region
7852
// FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
7853
// Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
7854
// Used by:
7855
// - Mouse wheel scrolling + many other things
7856
window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
7857
window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
7858
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)));
7859
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)));
7860
7861
// Setup drawing context
7862
// (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.)
7863
window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
7864
window->DC.GroupOffset.x = 0.0f;
7865
window->DC.ColumnsOffset.x = 0.0f;
7866
7867
// Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
7868
// 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.
7869
double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
7870
double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
7871
window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
7872
window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
7873
window->DC.CursorPos = window->DC.CursorStartPos;
7874
window->DC.CursorPosPrevLine = window->DC.CursorPos;
7875
window->DC.CursorMaxPos = window->DC.CursorStartPos;
7876
window->DC.IdealMaxPos = window->DC.CursorStartPos;
7877
window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
7878
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
7879
window->DC.IsSameLine = window->DC.IsSetPos = false;
7880
7881
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7882
window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
7883
window->DC.NavLayersActiveMaskNext = 0x00;
7884
window->DC.NavIsScrollPushableX = true;
7885
window->DC.NavHideHighlightOneFrame = false;
7886
window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
7887
7888
window->DC.MenuBarAppending = false;
7889
window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
7890
window->DC.TreeDepth = 0;
7891
window->DC.TreeHasStackDataDepthMask = window->DC.TreeRecordsClippedNodesY2Mask = 0x00;
7892
window->DC.ChildWindows.resize(0);
7893
window->DC.StateStorage = &window->StateStorage;
7894
window->DC.CurrentColumns = NULL;
7895
window->DC.LayoutType = ImGuiLayoutType_Vertical;
7896
window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
7897
7898
// Default item width. Make it proportional to window size if window manually resizes
7899
if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
7900
window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
7901
else
7902
window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
7903
window->DC.ItemWidth = window->ItemWidthDefault;
7904
window->DC.TextWrapPos = -1.0f; // disabled
7905
window->DC.ItemWidthStack.resize(0);
7906
window->DC.TextWrapPosStack.resize(0);
7907
if (flags & ImGuiWindowFlags_Modal)
7908
window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
7909
7910
if (window->AutoFitFramesX > 0)
7911
window->AutoFitFramesX--;
7912
if (window->AutoFitFramesY > 0)
7913
window->AutoFitFramesY--;
7914
7915
// Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
7916
// We ImGuiFocusRequestFlags_UnlessBelowModal to:
7917
// - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
7918
// - Position window behind the modal that is not a begin-parent of this window.
7919
if (want_focus)
7920
FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
7921
if (want_focus && window == g.NavWindow)
7922
NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
7923
7924
// Pressing CTRL+C copy window content into the clipboard
7925
// [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.
7926
// [EXPERIMENTAL] Text outputs has many issues.
7927
if (g.IO.ConfigWindowsCopyContentsWithCtrlC)
7928
if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C))
7929
LogToClipboard(0);
7930
7931
// Title bar
7932
if (!(flags & ImGuiWindowFlags_NoTitleBar))
7933
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);
7934
7935
// Clear hit test shape every frame
7936
window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
7937
7938
if (flags & ImGuiWindowFlags_Tooltip)
7939
g.TooltipPreviousWindow = window;
7940
7941
// We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
7942
// This is useful to allow creating context menus on title bar only, etc.
7943
window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None;
7944
window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
7945
SetLastItemDataForWindow(window, title_bar_rect);
7946
7947
// [DEBUG]
7948
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
7949
if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
7950
DebugLocateItemResolveWithLastItem();
7951
#endif
7952
7953
// [Test Engine] Register title bar / tab with MoveId.
7954
#ifdef IMGUI_ENABLE_TEST_ENGINE
7955
if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
7956
{
7957
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7958
IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
7959
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7960
}
7961
#endif
7962
}
7963
else
7964
{
7965
// Skip refresh always mark active
7966
if (window->SkipRefresh)
7967
SetWindowActiveForSkipRefresh(window);
7968
7969
// Append
7970
SetCurrentWindow(window);
7971
SetLastItemDataForWindow(window, window->TitleBarRect());
7972
}
7973
7974
if (!window->SkipRefresh)
7975
PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
7976
7977
// 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)
7978
window->WriteAccessed = false;
7979
window->BeginCount++;
7980
g.NextWindowData.ClearFlags();
7981
7982
// Update visibility
7983
if (first_begin_of_the_frame && !window->SkipRefresh)
7984
{
7985
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
7986
{
7987
// Child window can be out of sight and have "negative" clip windows.
7988
// Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
7989
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
7990
const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
7991
if (!g.LogEnabled && !nav_request)
7992
if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
7993
{
7994
if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7995
window->HiddenFramesCannotSkipItems = 1;
7996
else
7997
window->HiddenFramesCanSkipItems = 1;
7998
}
7999
8000
// Hide along with parent or if parent is collapsed
8001
if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
8002
window->HiddenFramesCanSkipItems = 1;
8003
if (parent_window && parent_window->HiddenFramesCannotSkipItems > 0)
8004
window->HiddenFramesCannotSkipItems = 1;
8005
}
8006
8007
// 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)
8008
if (style.Alpha <= 0.0f)
8009
window->HiddenFramesCanSkipItems = 1;
8010
8011
// Update the Hidden flag
8012
bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
8013
window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
8014
8015
// Disable inputs for requested number of frames
8016
if (window->DisableInputsFrames > 0)
8017
{
8018
window->DisableInputsFrames--;
8019
window->Flags |= ImGuiWindowFlags_NoInputs;
8020
}
8021
8022
// Update the SkipItems flag, used to early out of all items functions (no layout required)
8023
bool skip_items = false;
8024
if (window->Collapsed || !window->Active || hidden_regular)
8025
if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
8026
skip_items = true;
8027
window->SkipItems = skip_items;
8028
}
8029
else if (first_begin_of_the_frame)
8030
{
8031
// Skip refresh mode
8032
window->SkipItems = true;
8033
}
8034
8035
// [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
8036
// (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
8037
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8038
if (!window->IsFallbackWindow)
8039
if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
8040
{
8041
if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
8042
if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
8043
return false;
8044
}
8045
#endif
8046
8047
return !window->SkipItems;
8048
}
8049
8050
void ImGui::End()
8051
{
8052
ImGuiContext& g = *GImGui;
8053
ImGuiWindow* window = g.CurrentWindow;
8054
8055
// Error checking: verify that user hasn't called End() too many times!
8056
if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
8057
{
8058
IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
8059
return;
8060
}
8061
ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
8062
8063
// Error checking: verify that user doesn't directly call End() on a child window.
8064
if (window->Flags & ImGuiWindowFlags_ChildWindow)
8065
IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!");
8066
8067
// Close anything that is open
8068
if (window->DC.CurrentColumns)
8069
EndColumns();
8070
if (!window->SkipRefresh)
8071
PopClipRect(); // Inner window clip rectangle
8072
PopFocusScope();
8073
if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
8074
EndDisabledOverrideReenable();
8075
8076
if (window->SkipRefresh)
8077
{
8078
IM_ASSERT(window->DrawList == NULL);
8079
window->DrawList = &window->DrawListInst;
8080
}
8081
8082
// Stop logging
8083
if (g.LogWindow == window) // FIXME: add more options for scope of logging
8084
LogFinish();
8085
8086
if (window->DC.IsSetPos)
8087
ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
8088
8089
// Pop from window stack
8090
g.LastItemData = window_stack_data.ParentLastItemDataBackup;
8091
if (window->Flags & ImGuiWindowFlags_ChildMenu)
8092
g.BeginMenuDepth--;
8093
if (window->Flags & ImGuiWindowFlags_Popup)
8094
g.BeginPopupStack.pop_back();
8095
8096
// Error handling, state recovery
8097
if (g.IO.ConfigErrorRecovery)
8098
ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
8099
8100
g.CurrentWindowStack.pop_back();
8101
SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
8102
}
8103
8104
void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
8105
{
8106
ImGuiContext& g = *GImGui;
8107
ImGuiItemFlags item_flags = g.CurrentItemFlags;
8108
IM_ASSERT(item_flags == g.ItemFlagsStack.back());
8109
if (enabled)
8110
item_flags |= option;
8111
else
8112
item_flags &= ~option;
8113
g.CurrentItemFlags = item_flags;
8114
g.ItemFlagsStack.push_back(item_flags);
8115
}
8116
8117
void ImGui::PopItemFlag()
8118
{
8119
ImGuiContext& g = *GImGui;
8120
if (g.ItemFlagsStack.Size <= 1)
8121
{
8122
IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
8123
return;
8124
}
8125
g.ItemFlagsStack.pop_back();
8126
g.CurrentItemFlags = g.ItemFlagsStack.back();
8127
}
8128
8129
// BeginDisabled()/EndDisabled()
8130
// - 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)
8131
// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
8132
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
8133
// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
8134
// (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)
8135
// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
8136
void ImGui::BeginDisabled(bool disabled)
8137
{
8138
ImGuiContext& g = *GImGui;
8139
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8140
if (!was_disabled && disabled)
8141
{
8142
g.DisabledAlphaBackup = g.Style.Alpha;
8143
g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
8144
}
8145
if (was_disabled || disabled)
8146
g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
8147
g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
8148
g.DisabledStackSize++;
8149
}
8150
8151
void ImGui::EndDisabled()
8152
{
8153
ImGuiContext& g = *GImGui;
8154
if (g.DisabledStackSize <= 0)
8155
{
8156
IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
8157
return;
8158
}
8159
g.DisabledStackSize--;
8160
bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8161
//PopItemFlag();
8162
g.ItemFlagsStack.pop_back();
8163
g.CurrentItemFlags = g.ItemFlagsStack.back();
8164
if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
8165
g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
8166
}
8167
8168
// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
8169
// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
8170
// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
8171
void ImGui::BeginDisabledOverrideReenable()
8172
{
8173
ImGuiContext& g = *GImGui;
8174
IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
8175
g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup = g.Style.Alpha;
8176
g.Style.Alpha = g.DisabledAlphaBackup;
8177
g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
8178
g.ItemFlagsStack.push_back(g.CurrentItemFlags);
8179
g.DisabledStackSize++;
8180
}
8181
8182
void ImGui::EndDisabledOverrideReenable()
8183
{
8184
ImGuiContext& g = *GImGui;
8185
g.DisabledStackSize--;
8186
IM_ASSERT(g.DisabledStackSize > 0);
8187
g.ItemFlagsStack.pop_back();
8188
g.CurrentItemFlags = g.ItemFlagsStack.back();
8189
g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup;
8190
}
8191
8192
void ImGui::PushTextWrapPos(float wrap_pos_x)
8193
{
8194
ImGuiContext& g = *GImGui;
8195
ImGuiWindow* window = g.CurrentWindow;
8196
window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
8197
window->DC.TextWrapPos = wrap_pos_x;
8198
}
8199
8200
void ImGui::PopTextWrapPos()
8201
{
8202
ImGuiContext& g = *GImGui;
8203
ImGuiWindow* window = g.CurrentWindow;
8204
if (window->DC.TextWrapPosStack.Size <= 0)
8205
{
8206
IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
8207
return;
8208
}
8209
window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
8210
window->DC.TextWrapPosStack.pop_back();
8211
}
8212
8213
static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)
8214
{
8215
ImGuiWindow* last_window = NULL;
8216
while (last_window != window)
8217
{
8218
last_window = window;
8219
window = window->RootWindow;
8220
if (popup_hierarchy)
8221
window = window->RootWindowPopupTree;
8222
}
8223
return window;
8224
}
8225
8226
bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy)
8227
{
8228
ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);
8229
if (window_root == potential_parent)
8230
return true;
8231
while (window != NULL)
8232
{
8233
if (window == potential_parent)
8234
return true;
8235
if (window == window_root) // end of chain
8236
return false;
8237
window = window->ParentWindow;
8238
}
8239
return false;
8240
}
8241
8242
bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
8243
{
8244
if (window->RootWindow == potential_parent)
8245
return true;
8246
while (window != NULL)
8247
{
8248
if (window == potential_parent)
8249
return true;
8250
window = window->ParentWindowInBeginStack;
8251
}
8252
return false;
8253
}
8254
8255
bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
8256
{
8257
ImGuiContext& g = *GImGui;
8258
8259
// 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
8260
const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
8261
if (display_layer_delta != 0)
8262
return display_layer_delta > 0;
8263
8264
for (int i = g.Windows.Size - 1; i >= 0; i--)
8265
{
8266
ImGuiWindow* candidate_window = g.Windows[i];
8267
if (candidate_window == potential_above)
8268
return true;
8269
if (candidate_window == potential_below)
8270
return false;
8271
}
8272
return false;
8273
}
8274
8275
// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
8276
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
8277
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
8278
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
8279
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
8280
{
8281
ImGuiContext& g = *GImGui;
8282
IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
8283
8284
ImGuiWindow* ref_window = g.HoveredWindow;
8285
ImGuiWindow* cur_window = g.CurrentWindow;
8286
if (ref_window == NULL)
8287
return false;
8288
8289
if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
8290
{
8291
IM_ASSERT(cur_window); // Not inside a Begin()/End()
8292
const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
8293
if (flags & ImGuiHoveredFlags_RootWindow)
8294
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
8295
8296
bool result;
8297
if (flags & ImGuiHoveredFlags_ChildWindows)
8298
result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
8299
else
8300
result = (ref_window == cur_window);
8301
if (!result)
8302
return false;
8303
}
8304
8305
if (!IsWindowContentHoverable(ref_window, flags))
8306
return false;
8307
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
8308
if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
8309
return false;
8310
8311
// When changing hovered window we requires a bit of stationary delay before activating hover timer.
8312
// FIXME: We don't support delay other than stationary one for now, other delay would need a way
8313
// to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
8314
// for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
8315
// We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
8316
if (flags & ImGuiHoveredFlags_ForTooltip)
8317
flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
8318
if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
8319
return false;
8320
8321
return true;
8322
}
8323
8324
float ImGui::GetWindowWidth()
8325
{
8326
ImGuiWindow* window = GImGui->CurrentWindow;
8327
return window->Size.x;
8328
}
8329
8330
float ImGui::GetWindowHeight()
8331
{
8332
ImGuiWindow* window = GImGui->CurrentWindow;
8333
return window->Size.y;
8334
}
8335
8336
ImVec2 ImGui::GetWindowPos()
8337
{
8338
ImGuiContext& g = *GImGui;
8339
ImGuiWindow* window = g.CurrentWindow;
8340
return window->Pos;
8341
}
8342
8343
void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
8344
{
8345
// Test condition (NB: bit 0 is always true) and clear flags for next time
8346
if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
8347
return;
8348
8349
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8350
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8351
window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
8352
8353
// Set
8354
const ImVec2 old_pos = window->Pos;
8355
window->Pos = ImTrunc(pos);
8356
ImVec2 offset = window->Pos - old_pos;
8357
if (offset.x == 0.0f && offset.y == 0.0f)
8358
return;
8359
MarkIniSettingsDirty(window);
8360
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
8361
window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
8362
window->DC.IdealMaxPos += offset;
8363
window->DC.CursorStartPos += offset;
8364
}
8365
8366
void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
8367
{
8368
ImGuiWindow* window = GetCurrentWindowRead();
8369
SetWindowPos(window, pos, cond);
8370
}
8371
8372
void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
8373
{
8374
if (ImGuiWindow* window = FindWindowByName(name))
8375
SetWindowPos(window, pos, cond);
8376
}
8377
8378
ImVec2 ImGui::GetWindowSize()
8379
{
8380
ImGuiWindow* window = GetCurrentWindowRead();
8381
return window->Size;
8382
}
8383
8384
void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
8385
{
8386
// Test condition (NB: bit 0 is always true) and clear flags for next time
8387
if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
8388
return;
8389
8390
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8391
window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8392
8393
// Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
8394
if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8395
window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
8396
if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8397
window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
8398
8399
// Set
8400
ImVec2 old_size = window->SizeFull;
8401
if (size.x <= 0.0f)
8402
window->AutoFitOnlyGrows = false;
8403
else
8404
window->SizeFull.x = IM_TRUNC(size.x);
8405
if (size.y <= 0.0f)
8406
window->AutoFitOnlyGrows = false;
8407
else
8408
window->SizeFull.y = IM_TRUNC(size.y);
8409
if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
8410
MarkIniSettingsDirty(window);
8411
}
8412
8413
void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
8414
{
8415
SetWindowSize(GImGui->CurrentWindow, size, cond);
8416
}
8417
8418
void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
8419
{
8420
if (ImGuiWindow* window = FindWindowByName(name))
8421
SetWindowSize(window, size, cond);
8422
}
8423
8424
void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
8425
{
8426
// Test condition (NB: bit 0 is always true) and clear flags for next time
8427
if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
8428
return;
8429
window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8430
8431
// Queue applying in Begin()
8432
if (window->WantCollapseToggle)
8433
window->Collapsed ^= 1;
8434
window->WantCollapseToggle = (window->Collapsed != collapsed);
8435
}
8436
8437
void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
8438
{
8439
IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
8440
window->HitTestHoleSize = ImVec2ih(size);
8441
window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
8442
}
8443
8444
void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
8445
{
8446
window->Hidden = window->SkipItems = true;
8447
window->HiddenFramesCanSkipItems = 1;
8448
}
8449
8450
void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
8451
{
8452
SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
8453
}
8454
8455
bool ImGui::IsWindowCollapsed()
8456
{
8457
ImGuiWindow* window = GetCurrentWindowRead();
8458
return window->Collapsed;
8459
}
8460
8461
bool ImGui::IsWindowAppearing()
8462
{
8463
ImGuiWindow* window = GetCurrentWindowRead();
8464
return window->Appearing;
8465
}
8466
8467
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
8468
{
8469
if (ImGuiWindow* window = FindWindowByName(name))
8470
SetWindowCollapsed(window, collapsed, cond);
8471
}
8472
8473
void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
8474
{
8475
ImGuiContext& g = *GImGui;
8476
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8477
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasPos;
8478
g.NextWindowData.PosVal = pos;
8479
g.NextWindowData.PosPivotVal = pivot;
8480
g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
8481
}
8482
8483
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
8484
{
8485
ImGuiContext& g = *GImGui;
8486
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8487
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSize;
8488
g.NextWindowData.SizeVal = size;
8489
g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
8490
}
8491
8492
// For each axis:
8493
// - 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.
8494
// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
8495
// - See "Demo->Examples->Constrained-resizing window" for examples.
8496
void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
8497
{
8498
ImGuiContext& g = *GImGui;
8499
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
8500
g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
8501
g.NextWindowData.SizeCallback = custom_callback;
8502
g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
8503
}
8504
8505
// Content size = inner scrollable rectangle, padded with WindowPadding.
8506
// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
8507
void ImGui::SetNextWindowContentSize(const ImVec2& size)
8508
{
8509
ImGuiContext& g = *GImGui;
8510
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasContentSize;
8511
g.NextWindowData.ContentSizeVal = ImTrunc(size);
8512
}
8513
8514
void ImGui::SetNextWindowScroll(const ImVec2& scroll)
8515
{
8516
ImGuiContext& g = *GImGui;
8517
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasScroll;
8518
g.NextWindowData.ScrollVal = scroll;
8519
}
8520
8521
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
8522
{
8523
ImGuiContext& g = *GImGui;
8524
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8525
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasCollapsed;
8526
g.NextWindowData.CollapsedVal = collapsed;
8527
g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
8528
}
8529
8530
void ImGui::SetNextWindowBgAlpha(float alpha)
8531
{
8532
ImGuiContext& g = *GImGui;
8533
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasBgAlpha;
8534
g.NextWindowData.BgAlphaVal = alpha;
8535
}
8536
8537
// This is experimental and meant to be a toy for exploring a future/wider range of features.
8538
void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
8539
{
8540
ImGuiContext& g = *GImGui;
8541
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
8542
g.NextWindowData.RefreshFlagsVal = flags;
8543
}
8544
8545
ImDrawList* ImGui::GetWindowDrawList()
8546
{
8547
ImGuiWindow* window = GetCurrentWindow();
8548
return window->DrawList;
8549
}
8550
8551
ImFont* ImGui::GetFont()
8552
{
8553
return GImGui->Font;
8554
}
8555
8556
ImFontBaked* ImGui::GetFontBaked()
8557
{
8558
return GImGui->FontBaked;
8559
}
8560
8561
// Get current font size (= height in pixels) of current font, with global scale factors applied.
8562
// - Use style.FontSizeBase to get value before global scale factors.
8563
// - recap: ImGui::GetFontSize() == style.FontSizeBase * (style.FontScaleMain * style.FontScaleDpi * other_scaling_factors)
8564
float ImGui::GetFontSize()
8565
{
8566
return GImGui->FontSize;
8567
}
8568
8569
float ImGui::GetFontWeight()
8570
{
8571
return GImGui->FontWeight;
8572
}
8573
8574
ImVec2 ImGui::GetFontTexUvWhitePixel()
8575
{
8576
return GImGui->DrawListSharedData.TexUvWhitePixel;
8577
}
8578
8579
// Prefer using PushFont(NULL, style.FontSizeBase * factor), or use style.FontScaleMain to scale all windows.
8580
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8581
void ImGui::SetWindowFontScale(float scale)
8582
{
8583
IM_ASSERT(scale > 0.0f);
8584
ImGuiWindow* window = GetCurrentWindow();
8585
window->FontWindowScale = scale;
8586
UpdateCurrentFontSize(0.0f);
8587
}
8588
#endif
8589
8590
void ImGui::PushFocusScope(ImGuiID id)
8591
{
8592
ImGuiContext& g = *GImGui;
8593
ImGuiFocusScopeData data;
8594
data.ID = id;
8595
data.WindowID = g.CurrentWindow->ID;
8596
g.FocusScopeStack.push_back(data);
8597
g.CurrentFocusScopeId = id;
8598
}
8599
8600
void ImGui::PopFocusScope()
8601
{
8602
ImGuiContext& g = *GImGui;
8603
if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
8604
{
8605
IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
8606
return;
8607
}
8608
g.FocusScopeStack.pop_back();
8609
g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
8610
}
8611
8612
void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
8613
{
8614
ImGuiContext& g = *GImGui;
8615
g.NavFocusScopeId = focus_scope_id;
8616
g.NavFocusRoute.resize(0); // Invalidate
8617
if (focus_scope_id == 0)
8618
return;
8619
IM_ASSERT(g.NavWindow != NULL);
8620
8621
// Store current path (in reverse order)
8622
if (focus_scope_id == g.CurrentFocusScopeId)
8623
{
8624
// Top of focus stack contains local focus scopes inside current window
8625
for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
8626
g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
8627
}
8628
else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
8629
g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
8630
else
8631
return;
8632
8633
// Then follow on manually set ParentWindowForFocusRoute field (#6798)
8634
for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
8635
g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
8636
IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
8637
}
8638
8639
// Focus = move navigation cursor, set scrolling, set focus window.
8640
void ImGui::FocusItem()
8641
{
8642
ImGuiContext& g = *GImGui;
8643
ImGuiWindow* window = g.CurrentWindow;
8644
IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
8645
if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
8646
{
8647
IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
8648
return;
8649
}
8650
8651
ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
8652
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8653
SetNavWindow(window);
8654
NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
8655
NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8656
}
8657
8658
void ImGui::ActivateItemByID(ImGuiID id)
8659
{
8660
ImGuiContext& g = *GImGui;
8661
g.NavNextActivateId = id;
8662
g.NavNextActivateFlags = ImGuiActivateFlags_None;
8663
}
8664
8665
// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
8666
// But ActivateItem() should function without altering scroll/focus?
8667
void ImGui::SetKeyboardFocusHere(int offset)
8668
{
8669
ImGuiContext& g = *GImGui;
8670
ImGuiWindow* window = g.CurrentWindow;
8671
IM_ASSERT(offset >= -1); // -1 is allowed but not below
8672
IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
8673
8674
// It makes sense in the vast majority of cases to never interrupt a drag and drop.
8675
// When we refactor this function into ActivateItem() we may want to make this an option.
8676
// MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
8677
// is also automatically dropped in the event g.ActiveId is stolen.
8678
if (g.DragDropActive || g.MovingWindow != NULL)
8679
{
8680
IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
8681
return;
8682
}
8683
8684
SetNavWindow(window);
8685
8686
ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
8687
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8688
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.
8689
if (offset == -1)
8690
{
8691
NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8692
}
8693
else
8694
{
8695
g.NavTabbingDir = 1;
8696
g.NavTabbingCounter = offset + 1;
8697
}
8698
}
8699
8700
void ImGui::SetItemDefaultFocus()
8701
{
8702
ImGuiContext& g = *GImGui;
8703
ImGuiWindow* window = g.CurrentWindow;
8704
if (!window->Appearing)
8705
return;
8706
if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
8707
return;
8708
8709
g.NavInitRequest = false;
8710
NavApplyItemToResult(&g.NavInitResult);
8711
NavUpdateAnyRequestFlag();
8712
8713
// Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
8714
if (!window->ClipRect.Contains(g.LastItemData.Rect))
8715
ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
8716
}
8717
8718
void ImGui::SetStateStorage(ImGuiStorage* tree)
8719
{
8720
ImGuiWindow* window = GImGui->CurrentWindow;
8721
window->DC.StateStorage = tree ? tree : &window->StateStorage;
8722
}
8723
8724
ImGuiStorage* ImGui::GetStateStorage()
8725
{
8726
ImGuiWindow* window = GImGui->CurrentWindow;
8727
return window->DC.StateStorage;
8728
}
8729
8730
bool ImGui::IsRectVisible(const ImVec2& size)
8731
{
8732
ImGuiWindow* window = GImGui->CurrentWindow;
8733
return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
8734
}
8735
8736
bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
8737
{
8738
ImGuiWindow* window = GImGui->CurrentWindow;
8739
return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
8740
}
8741
8742
//-----------------------------------------------------------------------------
8743
// [SECTION] FONTS
8744
//-----------------------------------------------------------------------------
8745
// Most of the relevant font logic is in imgui_draw.cpp.
8746
// Those are high-level support functions.
8747
//-----------------------------------------------------------------------------
8748
// - UpdateFontsNewFrame() [Internal]
8749
// - UpdateFontsEndFrame() [Internal]
8750
// - GetDefaultFont() [Internal]
8751
// - RegisterUserTexture() [Internal]
8752
// - UnregisterUserTexture() [Internal]
8753
// - RegisterFontAtlas() [Internal]
8754
// - UnregisterFontAtlas() [Internal]
8755
// - SetCurrentFont() [Internal]
8756
// - UpdateCurrentFontSize() [Internal]
8757
// - SetFontRasterizerDensity() [Internal]
8758
// - PushFont()
8759
// - PopFont()
8760
//-----------------------------------------------------------------------------
8761
8762
void ImGui::UpdateFontsNewFrame()
8763
{
8764
ImGuiContext& g = *GImGui;
8765
if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8766
for (ImFontAtlas* atlas : g.FontAtlases)
8767
atlas->Locked = true;
8768
8769
if (g.Style._NextFrameFontSizeBase != 0.0f)
8770
{
8771
g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase;
8772
g.Style._NextFrameFontSizeBase = 0.0f;
8773
}
8774
8775
// Apply default font size the first time
8776
ImFont* font = ImGui::GetDefaultFont();
8777
if (g.Style.FontSizeBase <= 0.0f)
8778
g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE);
8779
8780
// Set initial font
8781
g.Font = font;
8782
g.FontSizeBase = g.Style.FontSizeBase;
8783
g.FontSize = 0.0f;
8784
ImFontStackData font_stack_data = { font, g.Style.FontSizeBase, g.Style.FontSizeBase }; // <--- Will restore FontSize
8785
SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0f, font_stack_data.FontWeight); // <--- but use 0.0f to enable scale
8786
g.FontStack.push_back(font_stack_data);
8787
IM_ASSERT(g.Font->IsLoaded());
8788
}
8789
8790
void ImGui::UpdateFontsEndFrame()
8791
{
8792
PopFont();
8793
}
8794
8795
ImFont* ImGui::GetDefaultFont()
8796
{
8797
ImGuiContext& g = *GImGui;
8798
ImFontAtlas* atlas = g.IO.Fonts;
8799
if (atlas->Builder == NULL || atlas->Fonts.Size == 0)
8800
ImFontAtlasBuildMain(atlas);
8801
return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];
8802
}
8803
8804
void ImGui::RegisterUserTexture(ImTextureData* tex)
8805
{
8806
ImGuiContext& g = *GImGui;
8807
IM_ASSERT(tex->RefCount > 0);
8808
g.UserTextures.push_back(tex);
8809
}
8810
8811
void ImGui::UnregisterUserTexture(ImTextureData* tex)
8812
{
8813
ImGuiContext& g = *GImGui;
8814
g.UserTextures.find_erase(tex);
8815
}
8816
8817
void ImGui::RegisterFontAtlas(ImFontAtlas* atlas)
8818
{
8819
ImGuiContext& g = *GImGui;
8820
if (g.FontAtlases.Size == 0)
8821
IM_ASSERT(atlas == g.IO.Fonts);
8822
atlas->RefCount++;
8823
g.FontAtlases.push_back(atlas);
8824
ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData);
8825
}
8826
8827
void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas)
8828
{
8829
ImGuiContext& g = *GImGui;
8830
IM_ASSERT(atlas->RefCount > 0);
8831
ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData);
8832
g.FontAtlases.find_erase(atlas);
8833
atlas->RefCount--;
8834
}
8835
8836
// Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList.
8837
// - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls.
8838
// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
8839
// - Some code paths never really fully worked with multiple atlas textures.
8840
// - The right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture()
8841
// the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
8842
// because we have a concrete need and a test bed for multiple atlas textures.
8843
// FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ?
8844
void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling, float font_weight)
8845
{
8846
ImGuiContext& g = *GImGui;
8847
g.Font = font;
8848
g.FontSizeBase = font_size_before_scaling;
8849
g.FontWeight = font_weight;
8850
g.DrawListSharedData.FontWeight = g.FontWeight;
8851
UpdateCurrentFontSize(font_size_after_scaling);
8852
8853
if (font != NULL)
8854
{
8855
IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
8856
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8857
IM_ASSERT(font->Scale > 0.0f);
8858
#endif
8859
ImFontAtlas* atlas = font->ContainerAtlas;
8860
g.DrawListSharedData.FontAtlas = atlas;
8861
g.DrawListSharedData.Font = font;
8862
ImFontAtlasUpdateDrawListsSharedData(atlas);
8863
if (g.CurrentWindow != NULL)
8864
g.CurrentWindow->DrawList->_SetTexture(atlas->TexRef);
8865
}
8866
}
8867
8868
void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)
8869
{
8870
ImGuiContext& g = *GImGui;
8871
ImGuiWindow* window = g.CurrentWindow;
8872
8873
g.Style.FontSizeBase = g.FontSizeBase;
8874
8875
// Early out to avoid hidden window keeping bakes referenced and out of GC reach.
8876
// However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching, so for now we null it.
8877
// FIXME: perhaps g.FontSize should be updated?
8878
if (window != NULL && window->SkipItems)
8879
if (g.CurrentTable == NULL || g.CurrentTable->CurrentColumn != -1) // See 8465#issuecomment-2951509561. Ideally the SkipItems=true in tables would be amended with extra data.
8880
return;
8881
8882
// Restoring is pretty much only used by PopFont()
8883
float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f;
8884
if (final_size == 0.0f)
8885
{
8886
final_size = g.FontSizeBase;
8887
8888
// Global scale factors
8889
final_size *= g.Style.FontScaleMain; // Main global scale factor
8890
final_size *= g.Style.FontScaleDpi; // Per-monitor/viewport DPI scale factor, automatically updated when io.ConfigDpiScaleFonts is enabled.
8891
8892
// Window scale (mostly obsolete now)
8893
if (window != NULL)
8894
final_size *= window->FontWindowScale;
8895
8896
// Legacy scale factors
8897
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8898
final_size *= g.IO.FontGlobalScale; // Use style.FontScaleMain instead!
8899
if (g.Font != NULL)
8900
final_size *= g.Font->Scale; // Was never really useful.
8901
#endif
8902
}
8903
8904
// Round font size
8905
// - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
8906
// - We may support it better later and remove this rounding.
8907
final_size = GetRoundedFontSize(final_size);
8908
final_size = ImMax(1.0f, final_size);
8909
if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures))
8910
g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity;
8911
g.FontSize = final_size;
8912
g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size, g.FontWeight) : NULL;
8913
g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
8914
g.DrawListSharedData.FontSize = g.FontSize;
8915
g.DrawListSharedData.FontScale = g.FontBakedScale;
8916
}
8917
8918
// Exposed in case user may want to override setting density.
8919
// IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it.
8920
void ImGui::SetFontRasterizerDensity(float rasterizer_density)
8921
{
8922
ImGuiContext& g = *GImGui;
8923
IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures);
8924
if (g.FontRasterizerDensity == rasterizer_density)
8925
return;
8926
g.FontRasterizerDensity = rasterizer_density;
8927
UpdateCurrentFontSize(0.0f);
8928
}
8929
8930
// If you want to scale an existing font size! Read comments in imgui.h!
8931
void ImGui::PushFont(ImFont* font, float font_size_base, float font_weight)
8932
{
8933
ImGuiContext& g = *GImGui;
8934
if (font == NULL) // Before 1.92 (June 2025), PushFont(NULL) == PushFont(GetDefaultFont())
8935
font = g.Font;
8936
IM_ASSERT(font != NULL);
8937
IM_ASSERT(font_size_base >= 0.0f);
8938
8939
g.FontStack.push_back({ g.Font, g.FontSizeBase, g.FontSize, g.FontWeight });
8940
if (font_size_base == 0.0f)
8941
font_size_base = g.FontSizeBase; // Keep current font size
8942
if (font_weight <= 0.0f)
8943
font_weight = (font_weight == 0.0f) ? font->DefaultWeight : g.FontWeight;
8944
SetCurrentFont(font, font_size_base, 0.0f, font_weight);
8945
}
8946
8947
void ImGui::PopFont()
8948
{
8949
ImGuiContext& g = *GImGui;
8950
if (g.FontStack.Size <= 0)
8951
{
8952
IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
8953
return;
8954
}
8955
ImFontStackData* font_stack_data = &g.FontStack.back();
8956
SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling, font_stack_data->FontWeight);
8957
g.FontStack.pop_back();
8958
}
8959
8960
//-----------------------------------------------------------------------------
8961
// [SECTION] ID STACK
8962
//-----------------------------------------------------------------------------
8963
8964
// This is one of the very rare legacy case where we use ImGuiWindow methods,
8965
// it should ideally be flattened at some point but it's been used a lots by widgets.
8966
IM_MSVC_RUNTIME_CHECKS_OFF
8967
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
8968
{
8969
ImGuiID seed = IDStack.back();
8970
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
8971
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8972
ImGuiContext& g = *Ctx;
8973
if (g.DebugHookIdInfo == id)
8974
ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
8975
#endif
8976
return id;
8977
}
8978
8979
ImGuiID ImGuiWindow::GetID(const void* ptr)
8980
{
8981
ImGuiID seed = IDStack.back();
8982
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
8983
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8984
ImGuiContext& g = *Ctx;
8985
if (g.DebugHookIdInfo == id)
8986
ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
8987
#endif
8988
return id;
8989
}
8990
8991
ImGuiID ImGuiWindow::GetID(int n)
8992
{
8993
ImGuiID seed = IDStack.back();
8994
ImGuiID id = ImHashData(&n, sizeof(n), seed);
8995
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8996
ImGuiContext& g = *Ctx;
8997
if (g.DebugHookIdInfo == id)
8998
ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
8999
#endif
9000
return id;
9001
}
9002
9003
// This is only used in rare/specific situations to manufacture an ID out of nowhere.
9004
// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
9005
ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)
9006
{
9007
ImGuiID seed = IDStack.back();
9008
ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs);
9009
ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed);
9010
return id;
9011
}
9012
9013
// "
9014
ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
9015
{
9016
ImGuiID seed = IDStack.back();
9017
ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
9018
ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
9019
return id;
9020
}
9021
9022
void ImGui::PushID(const char* str_id)
9023
{
9024
ImGuiContext& g = *GImGui;
9025
ImGuiWindow* window = g.CurrentWindow;
9026
ImGuiID id = window->GetID(str_id);
9027
window->IDStack.push_back(id);
9028
}
9029
9030
void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
9031
{
9032
ImGuiContext& g = *GImGui;
9033
ImGuiWindow* window = g.CurrentWindow;
9034
ImGuiID id = window->GetID(str_id_begin, str_id_end);
9035
window->IDStack.push_back(id);
9036
}
9037
9038
void ImGui::PushID(const void* ptr_id)
9039
{
9040
ImGuiContext& g = *GImGui;
9041
ImGuiWindow* window = g.CurrentWindow;
9042
ImGuiID id = window->GetID(ptr_id);
9043
window->IDStack.push_back(id);
9044
}
9045
9046
void ImGui::PushID(int int_id)
9047
{
9048
ImGuiContext& g = *GImGui;
9049
ImGuiWindow* window = g.CurrentWindow;
9050
ImGuiID id = window->GetID(int_id);
9051
window->IDStack.push_back(id);
9052
}
9053
9054
// Push a given id value ignoring the ID stack as a seed.
9055
void ImGui::PushOverrideID(ImGuiID id)
9056
{
9057
ImGuiContext& g = *GImGui;
9058
ImGuiWindow* window = g.CurrentWindow;
9059
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9060
if (g.DebugHookIdInfo == id)
9061
DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
9062
#endif
9063
window->IDStack.push_back(id);
9064
}
9065
9066
// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
9067
// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
9068
// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
9069
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
9070
{
9071
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9072
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9073
ImGuiContext& g = *GImGui;
9074
if (g.DebugHookIdInfo == id)
9075
DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9076
#endif
9077
return id;
9078
}
9079
9080
ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
9081
{
9082
ImGuiID id = ImHashData(&n, sizeof(n), seed);
9083
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9084
ImGuiContext& g = *GImGui;
9085
if (g.DebugHookIdInfo == id)
9086
DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9087
#endif
9088
return id;
9089
}
9090
9091
void ImGui::PopID()
9092
{
9093
ImGuiWindow* window = GImGui->CurrentWindow;
9094
if (window->IDStack.Size <= 1)
9095
{
9096
IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
9097
return;
9098
}
9099
window->IDStack.pop_back();
9100
}
9101
9102
ImGuiID ImGui::GetID(const char* str_id)
9103
{
9104
ImGuiWindow* window = GImGui->CurrentWindow;
9105
return window->GetID(str_id);
9106
}
9107
9108
ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
9109
{
9110
ImGuiWindow* window = GImGui->CurrentWindow;
9111
return window->GetID(str_id_begin, str_id_end);
9112
}
9113
9114
ImGuiID ImGui::GetID(const void* ptr_id)
9115
{
9116
ImGuiWindow* window = GImGui->CurrentWindow;
9117
return window->GetID(ptr_id);
9118
}
9119
9120
ImGuiID ImGui::GetID(int int_id)
9121
{
9122
ImGuiWindow* window = GImGui->CurrentWindow;
9123
return window->GetID(int_id);
9124
}
9125
IM_MSVC_RUNTIME_CHECKS_RESTORE
9126
9127
//-----------------------------------------------------------------------------
9128
// [SECTION] INPUTS
9129
//-----------------------------------------------------------------------------
9130
// - GetModForLRModKey() [Internal]
9131
// - FixupKeyChord() [Internal]
9132
// - GetKeyData() [Internal]
9133
// - GetKeyIndex() [Internal]
9134
// - GetKeyName()
9135
// - GetKeyChordName() [Internal]
9136
// - CalcTypematicRepeatAmount() [Internal]
9137
// - GetTypematicRepeatRate() [Internal]
9138
// - GetKeyPressedAmount() [Internal]
9139
// - GetKeyMagnitude2d() [Internal]
9140
//-----------------------------------------------------------------------------
9141
// - UpdateKeyRoutingTable() [Internal]
9142
// - GetRoutingIdFromOwnerId() [Internal]
9143
// - GetShortcutRoutingData() [Internal]
9144
// - CalcRoutingScore() [Internal]
9145
// - SetShortcutRouting() [Internal]
9146
// - TestShortcutRouting() [Internal]
9147
//-----------------------------------------------------------------------------
9148
// - IsKeyDown()
9149
// - IsKeyPressed()
9150
// - IsKeyReleased()
9151
//-----------------------------------------------------------------------------
9152
// - IsMouseDown()
9153
// - IsMouseClicked()
9154
// - IsMouseReleased()
9155
// - IsMouseDoubleClicked()
9156
// - GetMouseClickedCount()
9157
// - IsMouseHoveringRect() [Internal]
9158
// - IsMouseDragPastThreshold() [Internal]
9159
// - IsMouseDragging()
9160
// - GetMousePos()
9161
// - SetMousePos() [Internal]
9162
// - GetMousePosOnOpeningCurrentPopup()
9163
// - IsMousePosValid()
9164
// - IsAnyMouseDown()
9165
// - GetMouseDragDelta()
9166
// - ResetMouseDragDelta()
9167
// - GetMouseCursor()
9168
// - SetMouseCursor()
9169
//-----------------------------------------------------------------------------
9170
// - UpdateAliasKey()
9171
// - GetMergedModsFromKeys()
9172
// - UpdateKeyboardInputs()
9173
// - UpdateMouseInputs()
9174
//-----------------------------------------------------------------------------
9175
// - LockWheelingWindow [Internal]
9176
// - FindBestWheelingWindow [Internal]
9177
// - UpdateMouseWheel() [Internal]
9178
//-----------------------------------------------------------------------------
9179
// - SetNextFrameWantCaptureKeyboard()
9180
// - SetNextFrameWantCaptureMouse()
9181
//-----------------------------------------------------------------------------
9182
// - GetInputSourceName() [Internal]
9183
// - DebugPrintInputEvent() [Internal]
9184
// - UpdateInputEvents() [Internal]
9185
//-----------------------------------------------------------------------------
9186
// - GetKeyOwner() [Internal]
9187
// - TestKeyOwner() [Internal]
9188
// - SetKeyOwner() [Internal]
9189
// - SetItemKeyOwner() [Internal]
9190
// - Shortcut() [Internal]
9191
//-----------------------------------------------------------------------------
9192
9193
static ImGuiKeyChord GetModForLRModKey(ImGuiKey key)
9194
{
9195
if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
9196
return ImGuiMod_Ctrl;
9197
if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
9198
return ImGuiMod_Shift;
9199
if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
9200
return ImGuiMod_Alt;
9201
if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
9202
return ImGuiMod_Super;
9203
return ImGuiMod_None;
9204
}
9205
9206
ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
9207
{
9208
// Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9209
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9210
if (IsLRModKey(key))
9211
key_chord |= GetModForLRModKey(key);
9212
return key_chord;
9213
}
9214
9215
ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
9216
{
9217
ImGuiContext& g = *ctx;
9218
9219
// Special storage location for mods
9220
if (key & ImGuiMod_Mask_)
9221
key = ConvertSingleModFlagToKey(key);
9222
9223
IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
9224
return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9225
}
9226
9227
// Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.
9228
static const char* const GKeyNames[] =
9229
{
9230
"Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
9231
"Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
9232
"LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
9233
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
9234
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9235
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
9236
"F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
9237
"Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
9238
"Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
9239
"Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
9240
"Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
9241
"KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
9242
"AppBack", "AppForward", "Oem102",
9243
"GamepadStart", "GamepadBack",
9244
"GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
9245
"GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
9246
"GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
9247
"GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
9248
"GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
9249
"MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
9250
"ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
9251
};
9252
IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
9253
9254
const char* ImGui::GetKeyName(ImGuiKey key)
9255
{
9256
if (key == ImGuiKey_None)
9257
return "None";
9258
IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
9259
if (key & ImGuiMod_Mask_)
9260
key = ConvertSingleModFlagToKey(key);
9261
if (!IsNamedKey(key))
9262
return "Unknown";
9263
9264
return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
9265
}
9266
9267
// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
9268
// Lifetime of return value: valid until next call to same function.
9269
const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
9270
{
9271
ImGuiContext& g = *GImGui;
9272
9273
const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9274
if (IsLRModKey(key))
9275
key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
9276
ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
9277
(key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
9278
(key_chord & ImGuiMod_Shift) ? "Shift+" : "",
9279
(key_chord & ImGuiMod_Alt) ? "Alt+" : "",
9280
(key_chord & ImGuiMod_Super) ? "Super+" : "",
9281
(key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
9282
size_t len;
9283
if (key == ImGuiKey_None && key_chord != 0)
9284
if ((len = ImStrlen(g.TempKeychordName)) != 0) // Remove trailing '+'
9285
g.TempKeychordName[len - 1] = 0;
9286
return g.TempKeychordName;
9287
}
9288
9289
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
9290
// t1 = current time (e.g.: g.Time)
9291
// An event is triggered at:
9292
// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
9293
int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
9294
{
9295
if (t1 == 0.0f)
9296
return 1;
9297
if (t0 >= t1)
9298
return 0;
9299
if (repeat_rate <= 0.0f)
9300
return (t0 < repeat_delay) && (t1 >= repeat_delay);
9301
const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
9302
const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
9303
const int count = count_t1 - count_t0;
9304
return count;
9305
}
9306
9307
void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
9308
{
9309
ImGuiContext& g = *GImGui;
9310
switch (flags & ImGuiInputFlags_RepeatRateMask_)
9311
{
9312
case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
9313
case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
9314
case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
9315
}
9316
}
9317
9318
// Return value representing the number of presses in the last time period, for the given repeat rate
9319
// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
9320
int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
9321
{
9322
ImGuiContext& g = *GImGui;
9323
const ImGuiKeyData* key_data = GetKeyData(key);
9324
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)
9325
return 0;
9326
const float t = key_data->DownDuration;
9327
return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
9328
}
9329
9330
// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
9331
ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
9332
{
9333
return ImVec2(
9334
GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
9335
GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
9336
}
9337
9338
// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
9339
// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D
9340
// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6
9341
// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
9342
static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
9343
{
9344
ImGuiContext& g = *GImGui;
9345
rt->EntriesNext.resize(0);
9346
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9347
{
9348
const int new_routing_start_idx = rt->EntriesNext.Size;
9349
ImGuiKeyRoutingData* routing_entry;
9350
for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
9351
{
9352
routing_entry = &rt->Entries[old_routing_idx];
9353
routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
9354
routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
9355
routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
9356
routing_entry->RoutingNextScore = 255;
9357
if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
9358
continue;
9359
rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
9360
9361
// Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
9362
// This is the result of previous frame's SetShortcutRouting() call.
9363
if (routing_entry->Mods == g.IO.KeyMods)
9364
{
9365
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
9366
if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
9367
{
9368
owner_data->OwnerCurr = routing_entry->RoutingCurr;
9369
//IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
9370
}
9371
}
9372
}
9373
9374
// Rewrite linked-list
9375
rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
9376
for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
9377
rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
9378
}
9379
rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
9380
}
9381
9382
// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
9383
static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
9384
{
9385
ImGuiContext& g = *GImGui;
9386
return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
9387
}
9388
9389
ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
9390
{
9391
// Majority of shortcuts will be Key + any number of Mods
9392
// We accept _Single_ mod with ImGuiKey_None.
9393
// - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal
9394
// - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal
9395
// - Shortcut(ImGuiMod_Ctrl); // Legal
9396
// - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal
9397
ImGuiContext& g = *GImGui;
9398
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
9399
ImGuiKeyRoutingData* routing_data;
9400
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9401
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9402
if (key == ImGuiKey_None)
9403
key = ConvertSingleModFlagToKey(mods);
9404
IM_ASSERT(IsNamedKey(key));
9405
9406
// Get (in the majority of case, the linked list will have one element so this should be 2 reads.
9407
// Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
9408
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
9409
{
9410
routing_data = &rt->Entries[idx];
9411
if (routing_data->Mods == mods)
9412
return routing_data;
9413
}
9414
9415
// Add to linked-list
9416
ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
9417
rt->Entries.push_back(ImGuiKeyRoutingData());
9418
routing_data = &rt->Entries[routing_data_idx];
9419
routing_data->Mods = (ImU16)mods;
9420
routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
9421
rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
9422
return routing_data;
9423
}
9424
9425
// Current score encoding (lower is highest priority):
9426
// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive
9427
// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active)
9428
// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused
9429
// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)
9430
// - 254: ImGuiInputFlags_RouteGlobal
9431
// - 255: never route
9432
// 'flags' should include an explicit routing policy
9433
static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
9434
{
9435
ImGuiContext& g = *GImGui;
9436
if (flags & ImGuiInputFlags_RouteFocused)
9437
{
9438
// ActiveID gets top priority
9439
// (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
9440
if (owner_id != 0 && g.ActiveId == owner_id)
9441
return 1;
9442
9443
// Score based on distance to focused window (lower is better)
9444
// Assuming both windows are submitting a routing request,
9445
// - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
9446
// - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best)
9447
// Assuming only WindowA is submitting a routing request,
9448
// - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
9449
// This essentially follow the window->ParentWindowForFocusRoute chain.
9450
if (focus_scope_id == 0)
9451
return 255;
9452
for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
9453
if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
9454
return 3 + index_in_focus_path;
9455
return 255;
9456
}
9457
else if (flags & ImGuiInputFlags_RouteActive)
9458
{
9459
if (owner_id != 0 && g.ActiveId == owner_id)
9460
return 1;
9461
return 255;
9462
}
9463
else if (flags & ImGuiInputFlags_RouteGlobal)
9464
{
9465
if (flags & ImGuiInputFlags_RouteOverActive)
9466
return 0;
9467
if (flags & ImGuiInputFlags_RouteOverFocused)
9468
return 2;
9469
return 254;
9470
}
9471
IM_ASSERT(0);
9472
return 0;
9473
}
9474
9475
// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
9476
// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
9477
// - 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.
9478
static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
9479
{
9480
// Mimic 'ignore_char_inputs' logic in InputText()
9481
ImGuiContext& g = *GImGui;
9482
9483
// When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
9484
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9485
const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
9486
if (ignore_char_inputs)
9487
return false;
9488
9489
// Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
9490
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9491
if (key == ImGuiKey_None)
9492
return false;
9493
return g.KeysMayBeCharInput.TestBit(key);
9494
}
9495
9496
// Request a desired route for an input chord (key + mods).
9497
// Return true if the route is available this frame.
9498
// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
9499
// (Conceptually this does a "Submit for next frame" + "Test for current frame".
9500
// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
9501
bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
9502
{
9503
ImGuiContext& g = *GImGui;
9504
if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
9505
flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
9506
else
9507
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
9508
IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
9509
if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused))
9510
IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
9511
9512
// Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9513
key_chord = FixupKeyChord(key_chord);
9514
9515
// [DEBUG] Debug break requested by user
9516
if (g.DebugBreakInShortcutRouting == key_chord)
9517
IM_DEBUG_BREAK();
9518
9519
if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
9520
if (g.NavWindow == NULL)
9521
return false;
9522
9523
// Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
9524
if (flags & ImGuiInputFlags_RouteAlways)
9525
{
9526
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
9527
return true;
9528
}
9529
9530
// Specific culling when there's an active item.
9531
if (g.ActiveId != 0 && g.ActiveId != owner_id)
9532
{
9533
if (flags & ImGuiInputFlags_RouteActive)
9534
return false;
9535
9536
// Cull shortcuts with no modifiers when it could generate a character.
9537
// e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
9538
// but Shortcut(Ctrl+G) should generally trigger when InputText() is active.
9539
// TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
9540
// (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
9541
if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
9542
{
9543
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
9544
return false;
9545
}
9546
9547
// ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
9548
if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
9549
{
9550
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9551
if (key == ImGuiKey_None)
9552
key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
9553
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
9554
return false;
9555
}
9556
}
9557
9558
// Where do we evaluate route for?
9559
ImGuiID focus_scope_id = g.CurrentFocusScopeId;
9560
if (flags & ImGuiInputFlags_RouteFromRootWindow)
9561
focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
9562
9563
const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
9564
IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
9565
if (score == 255)
9566
return false;
9567
9568
// Submit routing for NEXT frame (assuming score is sufficient)
9569
// FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <).
9570
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
9571
//const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);
9572
if (score < routing_data->RoutingNextScore)
9573
{
9574
routing_data->RoutingNext = owner_id;
9575
routing_data->RoutingNextScore = (ImU8)score;
9576
}
9577
9578
// Return routing state for CURRENT frame
9579
if (routing_data->RoutingCurr == owner_id)
9580
IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
9581
return routing_data->RoutingCurr == owner_id;
9582
}
9583
9584
// Currently unused by core (but used by tests)
9585
// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
9586
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
9587
{
9588
const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
9589
key_chord = FixupKeyChord(key_chord);
9590
ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
9591
return routing_data->RoutingCurr == routing_id;
9592
}
9593
9594
// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
9595
// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
9596
bool ImGui::IsKeyDown(ImGuiKey key)
9597
{
9598
return IsKeyDown(key, ImGuiKeyOwner_Any);
9599
}
9600
9601
bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
9602
{
9603
const ImGuiKeyData* key_data = GetKeyData(key);
9604
if (!key_data->Down)
9605
return false;
9606
if (!TestKeyOwner(key, owner_id))
9607
return false;
9608
return true;
9609
}
9610
9611
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
9612
{
9613
return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9614
}
9615
9616
// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
9617
bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
9618
{
9619
const ImGuiKeyData* key_data = GetKeyData(key);
9620
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)
9621
return false;
9622
const float t = key_data->DownDuration;
9623
if (t < 0.0f)
9624
return false;
9625
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
9626
if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
9627
flags |= ImGuiInputFlags_Repeat;
9628
9629
bool pressed = (t == 0.0f);
9630
if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
9631
{
9632
float repeat_delay, repeat_rate;
9633
GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
9634
pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
9635
if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
9636
{
9637
// Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
9638
// Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
9639
ImGuiContext& g = *GImGui;
9640
double key_pressed_time = g.Time - t + 0.00001f;
9641
if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
9642
pressed = false;
9643
if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
9644
pressed = false;
9645
if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
9646
pressed = false;
9647
}
9648
}
9649
if (!pressed)
9650
return false;
9651
if (!TestKeyOwner(key, owner_id))
9652
return false;
9653
return true;
9654
}
9655
9656
bool ImGui::IsKeyReleased(ImGuiKey key)
9657
{
9658
return IsKeyReleased(key, ImGuiKeyOwner_Any);
9659
}
9660
9661
bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
9662
{
9663
const ImGuiKeyData* key_data = GetKeyData(key);
9664
if (key_data->DownDurationPrev < 0.0f || key_data->Down)
9665
return false;
9666
if (!TestKeyOwner(key, owner_id))
9667
return false;
9668
return true;
9669
}
9670
9671
bool ImGui::IsMouseDown(ImGuiMouseButton button)
9672
{
9673
ImGuiContext& g = *GImGui;
9674
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9675
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.
9676
}
9677
9678
bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
9679
{
9680
ImGuiContext& g = *GImGui;
9681
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9682
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.
9683
}
9684
9685
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
9686
{
9687
return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9688
}
9689
9690
bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
9691
{
9692
ImGuiContext& g = *GImGui;
9693
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9694
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)
9695
return false;
9696
const float t = g.IO.MouseDownDuration[button];
9697
if (t < 0.0f)
9698
return false;
9699
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
9700
9701
const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
9702
const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
9703
if (!pressed)
9704
return false;
9705
9706
if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
9707
return false;
9708
9709
return true;
9710
}
9711
9712
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
9713
{
9714
ImGuiContext& g = *GImGui;
9715
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9716
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
9717
}
9718
9719
bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
9720
{
9721
ImGuiContext& g = *GImGui;
9722
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9723
return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
9724
}
9725
9726
// Use if you absolutely need to distinguish single-click from double-click by introducing a delay.
9727
// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test.
9728
// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
9729
bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay)
9730
{
9731
ImGuiContext& g = *GImGui;
9732
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9733
const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]);
9734
return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
9735
}
9736
9737
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
9738
{
9739
ImGuiContext& g = *GImGui;
9740
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9741
return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
9742
}
9743
9744
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
9745
{
9746
ImGuiContext& g = *GImGui;
9747
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9748
return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
9749
}
9750
9751
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
9752
{
9753
ImGuiContext& g = *GImGui;
9754
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9755
return g.IO.MouseClickedCount[button];
9756
}
9757
9758
// Test if mouse cursor is hovering given rectangle
9759
// NB- Rectangle is clipped by our current clip setting
9760
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
9761
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
9762
{
9763
ImGuiContext& g = *GImGui;
9764
9765
// Clip
9766
ImRect rect_clipped(r_min, r_max);
9767
if (clip)
9768
rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
9769
9770
// Hit testing, expanded for touch input
9771
if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
9772
return false;
9773
return true;
9774
}
9775
9776
// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
9777
// [Internal] This doesn't test if the button is pressed
9778
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
9779
{
9780
ImGuiContext& g = *GImGui;
9781
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9782
if (lock_threshold < 0.0f)
9783
lock_threshold = g.IO.MouseDragThreshold;
9784
return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
9785
}
9786
9787
bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
9788
{
9789
ImGuiContext& g = *GImGui;
9790
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9791
if (!g.IO.MouseDown[button])
9792
return false;
9793
return IsMouseDragPastThreshold(button, lock_threshold);
9794
}
9795
9796
ImVec2 ImGui::GetMousePos()
9797
{
9798
ImGuiContext& g = *GImGui;
9799
return g.IO.MousePos;
9800
}
9801
9802
// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
9803
// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
9804
void ImGui::TeleportMousePos(const ImVec2& pos)
9805
{
9806
ImGuiContext& g = *GImGui;
9807
g.IO.MousePos = g.IO.MousePosPrev = pos;
9808
g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
9809
g.IO.WantSetMousePos = true;
9810
//IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
9811
}
9812
9813
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
9814
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
9815
{
9816
ImGuiContext& g = *GImGui;
9817
if (g.BeginPopupStack.Size > 0)
9818
return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
9819
return g.IO.MousePos;
9820
}
9821
9822
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
9823
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
9824
{
9825
// The assert is only to silence a false-positive in XCode Static Analysis.
9826
// 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).
9827
IM_ASSERT(GImGui != NULL);
9828
const float MOUSE_INVALID = -256000.0f;
9829
ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
9830
return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
9831
}
9832
9833
// [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.
9834
bool ImGui::IsAnyMouseDown()
9835
{
9836
ImGuiContext& g = *GImGui;
9837
for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
9838
if (g.IO.MouseDown[n])
9839
return true;
9840
return false;
9841
}
9842
9843
// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
9844
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
9845
// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
9846
ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
9847
{
9848
ImGuiContext& g = *GImGui;
9849
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9850
if (lock_threshold < 0.0f)
9851
lock_threshold = g.IO.MouseDragThreshold;
9852
if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
9853
if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
9854
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
9855
return g.IO.MousePos - g.IO.MouseClickedPos[button];
9856
return ImVec2(0.0f, 0.0f);
9857
}
9858
9859
void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
9860
{
9861
ImGuiContext& g = *GImGui;
9862
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9863
// NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
9864
g.IO.MouseClickedPos[button] = g.IO.MousePos;
9865
}
9866
9867
// Get desired mouse cursor shape.
9868
// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
9869
// updated during the frame, and locked in EndFrame()/Render().
9870
// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
9871
ImGuiMouseCursor ImGui::GetMouseCursor()
9872
{
9873
ImGuiContext& g = *GImGui;
9874
return g.MouseCursor;
9875
}
9876
9877
// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
9878
// 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
9879
// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
9880
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
9881
{
9882
ImGuiContext& g = *GImGui;
9883
g.MouseCursor = cursor_type;
9884
}
9885
9886
static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
9887
{
9888
IM_ASSERT(ImGui::IsAliasKey(key));
9889
ImGuiKeyData* key_data = ImGui::GetKeyData(key);
9890
key_data->Down = v;
9891
key_data->AnalogValue = analog_value;
9892
}
9893
9894
// [Internal] Do not use directly
9895
static ImGuiKeyChord GetMergedModsFromKeys()
9896
{
9897
ImGuiKeyChord mods = 0;
9898
if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; }
9899
if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; }
9900
if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; }
9901
if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; }
9902
return mods;
9903
}
9904
9905
static void ImGui::UpdateKeyboardInputs()
9906
{
9907
ImGuiContext& g = *GImGui;
9908
ImGuiIO& io = g.IO;
9909
9910
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
9911
io.ClearInputKeys();
9912
9913
// Update aliases
9914
for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
9915
UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
9916
UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
9917
UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
9918
9919
// Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
9920
// - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array.
9921
// - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
9922
// So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
9923
const ImGuiKeyChord prev_key_mods = io.KeyMods;
9924
io.KeyMods = GetMergedModsFromKeys();
9925
io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
9926
io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
9927
io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
9928
io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
9929
if (prev_key_mods != io.KeyMods)
9930
g.LastKeyModsChangeTime = g.Time;
9931
if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
9932
g.LastKeyModsChangeFromNoneTime = g.Time;
9933
9934
// Clear gamepad data if disabled
9935
if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
9936
for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++)
9937
{
9938
io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false;
9939
io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f;
9940
}
9941
9942
// Update keys
9943
for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
9944
{
9945
ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9946
key_data->DownDurationPrev = key_data->DownDuration;
9947
key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
9948
if (key_data->DownDuration == 0.0f)
9949
{
9950
if (IsKeyboardKey((ImGuiKey)key))
9951
g.LastKeyboardKeyPressTime = g.Time;
9952
else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
9953
g.LastKeyboardKeyPressTime = g.Time;
9954
}
9955
}
9956
9957
// Update keys/input owner (named keys only): one entry per key
9958
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9959
{
9960
ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9961
ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
9962
owner_data->OwnerCurr = owner_data->OwnerNext;
9963
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.
9964
owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
9965
owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore
9966
}
9967
9968
// Update key routing (for e.g. shortcuts)
9969
UpdateKeyRoutingTable(&g.KeysRoutingTable);
9970
}
9971
9972
static void ImGui::UpdateMouseInputs()
9973
{
9974
ImGuiContext& g = *GImGui;
9975
ImGuiIO& io = g.IO;
9976
9977
// Mouse Wheel swapping flag
9978
// As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
9979
// - We avoid doing it on OSX as it the OS input layer handles this already.
9980
// - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
9981
// - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
9982
io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
9983
9984
// Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
9985
if (IsMousePosValid(&io.MousePos))
9986
io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
9987
9988
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
9989
if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
9990
io.MouseDelta = io.MousePos - io.MousePosPrev;
9991
else
9992
io.MouseDelta = ImVec2(0.0f, 0.0f);
9993
9994
// Update stationary timer.
9995
// FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
9996
const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
9997
const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
9998
g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
9999
//IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
10000
10001
// 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.
10002
if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
10003
g.NavHighlightItemUnderNav = false;
10004
10005
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
10006
{
10007
io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
10008
io.MouseClickedCount[i] = 0; // Will be filled below
10009
io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
10010
if (io.MouseReleased[i])
10011
io.MouseReleasedTime[i] = g.Time;
10012
io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
10013
io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
10014
if (io.MouseClicked[i])
10015
{
10016
bool is_repeated_click = false;
10017
if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
10018
{
10019
ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10020
if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
10021
is_repeated_click = true;
10022
}
10023
if (is_repeated_click)
10024
io.MouseClickedLastCount[i]++;
10025
else
10026
io.MouseClickedLastCount[i] = 1;
10027
io.MouseClickedTime[i] = g.Time;
10028
io.MouseClickedPos[i] = io.MousePos;
10029
io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
10030
io.MouseDragMaxDistanceSqr[i] = 0.0f;
10031
}
10032
else if (io.MouseDown[i])
10033
{
10034
// Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
10035
float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f;
10036
io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos);
10037
}
10038
10039
// We provide io.MouseDoubleClicked[] as a legacy service
10040
io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
10041
10042
// Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
10043
if (io.MouseClicked[i])
10044
g.NavHighlightItemUnderNav = false;
10045
}
10046
}
10047
10048
static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
10049
{
10050
ImGuiContext& g = *GImGui;
10051
if (window)
10052
g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
10053
else
10054
g.WheelingWindowReleaseTimer = 0.0f;
10055
if (g.WheelingWindow == window)
10056
return;
10057
IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
10058
g.WheelingWindow = window;
10059
g.WheelingWindowRefMousePos = g.IO.MousePos;
10060
if (window == NULL)
10061
{
10062
g.WheelingWindowStartFrame = -1;
10063
g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
10064
}
10065
}
10066
10067
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
10068
{
10069
// For each axis, find window in the hierarchy that may want to use scrolling
10070
ImGuiContext& g = *GImGui;
10071
ImGuiWindow* windows[2] = { NULL, NULL };
10072
for (int axis = 0; axis < 2; axis++)
10073
if (wheel[axis] != 0.0f)
10074
for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
10075
{
10076
// Bubble up into parent window if:
10077
// - a child window doesn't allow any scrolling.
10078
// - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
10079
//// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
10080
const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
10081
const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
10082
//const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
10083
if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
10084
break; // select this window
10085
}
10086
if (windows[0] == NULL && windows[1] == NULL)
10087
return NULL;
10088
10089
// If there's only one window or only one axis then there's no ambiguity
10090
if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
10091
return windows[1] ? windows[1] : windows[0];
10092
10093
// If candidate are different windows we need to decide which one to prioritize
10094
// - First frame: only find a winner if one axis is zero.
10095
// - Subsequent frames: only find a winner when one is more than the other.
10096
if (g.WheelingWindowStartFrame == -1)
10097
g.WheelingWindowStartFrame = g.FrameCount;
10098
if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
10099
{
10100
g.WheelingWindowWheelRemainder = wheel;
10101
return NULL;
10102
}
10103
return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
10104
}
10105
10106
// Called by NewFrame()
10107
void ImGui::UpdateMouseWheel()
10108
{
10109
// Reset the locked window if we move the mouse or after the timer elapses.
10110
// 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)
10111
ImGuiContext& g = *GImGui;
10112
if (g.WheelingWindow != NULL)
10113
{
10114
g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
10115
if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
10116
g.WheelingWindowReleaseTimer = 0.0f;
10117
if (g.WheelingWindowReleaseTimer <= 0.0f)
10118
LockWheelingWindow(NULL, 0.0f);
10119
}
10120
10121
ImVec2 wheel;
10122
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
10123
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
10124
10125
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
10126
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
10127
if (!mouse_window || mouse_window->Collapsed)
10128
return;
10129
10130
// Zoom / Scale window
10131
// FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
10132
if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
10133
{
10134
LockWheelingWindow(mouse_window, wheel.y);
10135
ImGuiWindow* window = mouse_window;
10136
const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
10137
const float scale = new_font_scale / window->FontWindowScale;
10138
window->FontWindowScale = new_font_scale;
10139
if (window == window->RootWindow)
10140
{
10141
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
10142
SetWindowPos(window, window->Pos + offset, 0);
10143
window->Size = ImTrunc(window->Size * scale);
10144
window->SizeFull = ImTrunc(window->SizeFull * scale);
10145
}
10146
return;
10147
}
10148
if (g.IO.KeyCtrl)
10149
return;
10150
10151
// Mouse wheel scrolling
10152
// Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
10153
if (g.IO.MouseWheelRequestAxisSwap)
10154
wheel = ImVec2(wheel.y, 0.0f);
10155
10156
// Maintain a rough average of moving magnitude on both axes
10157
// FIXME: should by based on wall clock time rather than frame-counter
10158
g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
10159
g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
10160
10161
// In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
10162
wheel += g.WheelingWindowWheelRemainder;
10163
g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
10164
if (wheel.x == 0.0f && wheel.y == 0.0f)
10165
return;
10166
10167
// Mouse wheel scrolling: find target and apply
10168
// - don't renew lock if axis doesn't apply on the window.
10169
// - select a main axis when both axes are being moved.
10170
if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
10171
if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
10172
{
10173
bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
10174
if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
10175
do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
10176
if (do_scroll[ImGuiAxis_X])
10177
{
10178
LockWheelingWindow(window, wheel.x);
10179
float max_step = window->InnerRect.GetWidth() * 0.67f;
10180
float scroll_step = (window->ScrollStepSize.x != 0.0f) ? window->ScrollStepSize.x : ImTrunc(ImMin(2 * window->FontRefSize, max_step));
10181
SetScrollX(window, window->ScrollExpected.x - wheel.x * scroll_step);
10182
g.WheelingWindowScrolledFrame = g.FrameCount;
10183
}
10184
if (do_scroll[ImGuiAxis_Y])
10185
{
10186
LockWheelingWindow(window, wheel.y);
10187
float max_step = window->InnerRect.GetHeight() * 0.67f;
10188
float scroll_step = (window->ScrollStepSize.y != 0.0f) ? window->ScrollStepSize.y : ImTrunc(ImMin(5 * window->FontRefSize, max_step));
10189
SetScrollY(window, window->ScrollExpected.y - wheel.y * scroll_step);
10190
g.WheelingWindowScrolledFrame = g.FrameCount;
10191
}
10192
}
10193
}
10194
10195
void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
10196
{
10197
ImGuiContext& g = *GImGui;
10198
g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
10199
}
10200
10201
void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
10202
{
10203
ImGuiContext& g = *GImGui;
10204
g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
10205
}
10206
10207
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10208
static const char* GetInputSourceName(ImGuiInputSource source)
10209
{
10210
const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
10211
IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);
10212
return input_source_names[source];
10213
}
10214
static const char* GetMouseSourceName(ImGuiMouseSource source)
10215
{
10216
const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
10217
IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);
10218
return mouse_source_names[source];
10219
}
10220
static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
10221
{
10222
ImGuiContext& g = *GImGui;
10223
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; }
10224
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; }
10225
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; }
10226
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; }
10227
if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
10228
if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
10229
}
10230
#endif
10231
10232
// Process input queue
10233
// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
10234
// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
10235
// - 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)
10236
void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
10237
{
10238
ImGuiContext& g = *GImGui;
10239
ImGuiIO& io = g.IO;
10240
10241
// Only trickle chars<>key when working with InputText()
10242
// FIXME: InputText() could parse event trail?
10243
// FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
10244
const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
10245
10246
bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
10247
int mouse_button_changed = 0x00;
10248
ImBitArray<ImGuiKey_NamedKey_COUNT> key_changed_mask;
10249
10250
int event_n = 0;
10251
for (; event_n < g.InputEventsQueue.Size; event_n++)
10252
{
10253
ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
10254
if (e->Type == ImGuiInputEventType_MousePos)
10255
{
10256
if (g.IO.WantSetMousePos)
10257
continue;
10258
// Trickling Rule: Stop processing queued events if we already handled a mouse button change
10259
ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
10260
if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
10261
break;
10262
io.MousePos = event_pos;
10263
io.MouseSource = e->MousePos.MouseSource;
10264
mouse_moved = true;
10265
}
10266
else if (e->Type == ImGuiInputEventType_MouseButton)
10267
{
10268
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
10269
const ImGuiMouseButton button = e->MouseButton.Button;
10270
IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
10271
if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
10272
break;
10273
if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
10274
break;
10275
io.MouseDown[button] = e->MouseButton.Down;
10276
io.MouseSource = e->MouseButton.MouseSource;
10277
mouse_button_changed |= (1 << button);
10278
}
10279
else if (e->Type == ImGuiInputEventType_MouseWheel)
10280
{
10281
// Trickling Rule: Stop processing queued events if we got multiple action on the event
10282
if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
10283
break;
10284
io.MouseWheelH += e->MouseWheel.WheelX;
10285
io.MouseWheel += e->MouseWheel.WheelY;
10286
io.MouseSource = e->MouseWheel.MouseSource;
10287
mouse_wheeled = true;
10288
}
10289
else if (e->Type == ImGuiInputEventType_Key)
10290
{
10291
// Trickling Rule: Stop processing queued events if we got multiple action on the same button
10292
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10293
continue;
10294
ImGuiKey key = e->Key.Key;
10295
IM_ASSERT(key != ImGuiKey_None);
10296
ImGuiKeyData* key_data = GetKeyData(key);
10297
const int key_data_index = (int)(key_data - g.IO.KeysData);
10298
if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0))
10299
break;
10300
10301
const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key);
10302
if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))
10303
break;
10304
10305
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.
10306
{
10307
key_changed = true;
10308
key_changed_mask.SetBit(key_data_index);
10309
if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
10310
key_changed_nonchar = true;
10311
}
10312
10313
key_data->Down = e->Key.Down;
10314
key_data->AnalogValue = e->Key.AnalogValue;
10315
}
10316
else if (e->Type == ImGuiInputEventType_Text)
10317
{
10318
if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10319
continue;
10320
// Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
10321
if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
10322
break;
10323
if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)
10324
break;
10325
unsigned int c = e->Text.Char;
10326
io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
10327
if (trickle_interleaved_nonchar_keys_and_text)
10328
text_inputted = true;
10329
}
10330
else if (e->Type == ImGuiInputEventType_Focus)
10331
{
10332
// We intentionally overwrite this and process in NewFrame(), in order to give a chance
10333
// to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
10334
const bool focus_lost = !e->AppFocused.Focused;
10335
io.AppFocusLost = focus_lost;
10336
}
10337
else
10338
{
10339
IM_ASSERT(0 && "Unknown event!");
10340
}
10341
}
10342
10343
// Record trail (for domain-specific applications wanting to access a precise trail)
10344
//if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
10345
for (int n = 0; n < event_n; n++)
10346
g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
10347
10348
// [DEBUG]
10349
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10350
if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
10351
for (int n = 0; n < g.InputEventsQueue.Size; n++)
10352
DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
10353
#endif
10354
10355
// Remaining events will be processed on the next frame
10356
if (event_n == g.InputEventsQueue.Size)
10357
g.InputEventsQueue.resize(0);
10358
else
10359
g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
10360
10361
// Clear buttons state when focus is lost
10362
// - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
10363
// - we clear in EndFrame() and not now in order allow application/user code polling this flag
10364
// (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
10365
if (g.IO.AppFocusLost)
10366
{
10367
g.IO.ClearInputKeys();
10368
g.IO.ClearInputMouse();
10369
}
10370
}
10371
10372
ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
10373
{
10374
if (!IsNamedKeyOrMod(key))
10375
return ImGuiKeyOwner_NoOwner;
10376
10377
ImGuiContext& g = *GImGui;
10378
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10379
ImGuiID owner_id = owner_data->OwnerCurr;
10380
10381
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10382
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10383
return ImGuiKeyOwner_NoOwner;
10384
10385
return owner_id;
10386
}
10387
10388
// TestKeyOwner(..., ID) : (owner == None || owner == ID)
10389
// TestKeyOwner(..., None) : (owner == None)
10390
// TestKeyOwner(..., Any) : no owner test
10391
// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
10392
bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
10393
{
10394
if (!IsNamedKeyOrMod(key))
10395
return true;
10396
10397
ImGuiContext& g = *GImGui;
10398
if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10399
if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10400
return false;
10401
10402
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10403
if (owner_id == ImGuiKeyOwner_Any)
10404
return (owner_data->LockThisFrame == false);
10405
10406
// Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
10407
// are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
10408
// Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
10409
if (owner_data->OwnerCurr != owner_id)
10410
{
10411
if (owner_data->LockThisFrame)
10412
return false;
10413
if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
10414
return false;
10415
}
10416
10417
return true;
10418
}
10419
10420
// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
10421
// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
10422
// - SetKeyOwner(..., None) : clears owner
10423
// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
10424
// - SetKeyOwner(..., Any or None, Lock) : set lock
10425
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
10426
{
10427
ImGuiContext& g = *GImGui;
10428
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)
10429
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
10430
//IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
10431
10432
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10433
owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
10434
10435
// We cannot lock by default as it would likely break lots of legacy code.
10436
// In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
10437
owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
10438
owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
10439
}
10440
10441
// Rarely used helper
10442
void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
10443
{
10444
if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
10445
if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
10446
if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
10447
if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
10448
if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
10449
}
10450
10451
// This is more or less equivalent to:
10452
// if (IsItemHovered() || IsItemActive())
10453
// SetKeyOwner(key, GetItemID());
10454
// 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.
10455
// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
10456
// 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.
10457
void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
10458
{
10459
ImGuiContext& g = *GImGui;
10460
ImGuiID id = g.LastItemData.ID;
10461
if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
10462
return;
10463
if ((flags & ImGuiInputFlags_CondMask_) == 0)
10464
flags |= ImGuiInputFlags_CondDefault_;
10465
if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
10466
{
10467
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
10468
SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
10469
}
10470
}
10471
10472
void ImGui::SetItemKeyOwner(ImGuiKey key)
10473
{
10474
SetItemKeyOwner(key, ImGuiInputFlags_None);
10475
}
10476
10477
// This is the only public API until we expose owner_id versions of the API as replacements.
10478
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
10479
{
10480
return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10481
}
10482
10483
// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
10484
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10485
{
10486
ImGuiContext& g = *GImGui;
10487
key_chord = FixupKeyChord(key_chord);
10488
ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
10489
if (g.IO.KeyMods != mods)
10490
return false;
10491
10492
// Special storage location for mods
10493
ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
10494
if (key == ImGuiKey_None)
10495
key = ConvertSingleModFlagToKey(mods);
10496
if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
10497
return false;
10498
return true;
10499
}
10500
10501
void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10502
{
10503
ImGuiContext& g = *GImGui;
10504
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
10505
g.NextItemData.Shortcut = key_chord;
10506
g.NextItemData.ShortcutFlags = flags;
10507
}
10508
10509
// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
10510
void ImGui::ItemHandleShortcut(ImGuiID id)
10511
{
10512
ImGuiContext& g = *GImGui;
10513
ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
10514
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
10515
10516
if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
10517
return;
10518
if (flags & ImGuiInputFlags_Tooltip)
10519
{
10520
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
10521
g.LastItemData.Shortcut = g.NextItemData.Shortcut;
10522
}
10523
if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
10524
return;
10525
10526
// FIXME: Generalize Activation queue?
10527
g.NavActivateId = id; // Will effectively disable clipping.
10528
g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
10529
//if (g.ActiveId == 0 || g.ActiveId == id)
10530
g.NavActivateDownId = g.NavActivatePressedId = id;
10531
NavHighlightActivated(id);
10532
}
10533
10534
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10535
{
10536
return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
10537
}
10538
10539
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10540
{
10541
ImGuiContext& g = *GImGui;
10542
//IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
10543
10544
// When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
10545
if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
10546
flags |= ImGuiInputFlags_RouteFocused;
10547
10548
// Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
10549
// Effectively makes Shortcut() always input-owner aware.
10550
if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
10551
owner_id = GetRoutingIdFromOwnerId(owner_id);
10552
10553
if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10554
return false;
10555
10556
// Submit route
10557
if (!SetShortcutRouting(key_chord, flags, owner_id))
10558
return false;
10559
10560
// Default repeat behavior for Shortcut()
10561
// So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
10562
if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
10563
flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
10564
10565
if (!IsKeyChordPressed(key_chord, flags, owner_id))
10566
return false;
10567
10568
// Claim mods during the press
10569
SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
10570
10571
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
10572
return true;
10573
}
10574
10575
//-----------------------------------------------------------------------------
10576
// [SECTION] ERROR CHECKING, STATE RECOVERY
10577
//-----------------------------------------------------------------------------
10578
// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
10579
// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10580
// - ErrorCheckNewFrameSanityChecks()
10581
// - ErrorCheckEndFrameSanityChecks()
10582
// - ErrorRecoveryStoreState()
10583
// - ErrorRecoveryTryToRecoverState()
10584
// - ErrorRecoveryTryToRecoverWindowState()
10585
// - ErrorLog()
10586
//-----------------------------------------------------------------------------
10587
10588
// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
10589
// Called by IMGUI_CHECKVERSION().
10590
// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
10591
// If this triggers you have mismatched headers and compiled code versions.
10592
// - It could be because of a build issue (using new headers with old compiled code)
10593
// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
10594
// THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
10595
// Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
10596
// Otherwise it is possible that different compilation units would see different structure layout.
10597
// If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
10598
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)
10599
{
10600
bool error = false;
10601
if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
10602
if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
10603
if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
10604
if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
10605
if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
10606
if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
10607
if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
10608
return !error;
10609
}
10610
10611
// Until 1.89 (August 2022, IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos()/SetCursorScreenPos()
10612
// to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing
10613
// windows, table column contents size used for auto-resizing columns, group size).
10614
// This was causing issues and ambiguities and we needed to retire that.
10615
// From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED.
10616
//
10617
// Previously this would make the window content size ~200x200:
10618
// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK ANYMORE
10619
// Instead, please submit an item:
10620
// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
10621
// Alternative:
10622
// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
10623
//
10624
// The assert below detects when the _last_ call in a window was a SetCursorPos() not followed by an Item,
10625
// and with a position that would grow the parent contents size.
10626
//
10627
// Advanced:
10628
// - For reference, old logic was causing issues because it meant that SetCursorScreenPos(GetCursorScreenPos())
10629
// had a side-effect on layout! In particular this caused problem to compute group boundaries.
10630
// e.g. BeginGroup() + SomeItem() + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() would cause the
10631
// group to be taller because auto-sizing generally adds padding on bottom and right side.
10632
// - While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect.
10633
// Using vertical alignment patterns would frequently trigger this sorts of issue.
10634
// - See https://github.com/ocornut/imgui/issues/5548 for more details.
10635
void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10636
{
10637
ImGuiContext& g = *GImGui;
10638
ImGuiWindow* window = g.CurrentWindow;
10639
IM_ASSERT(window->DC.IsSetPos);
10640
window->DC.IsSetPos = false;
10641
if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
10642
return;
10643
if (window->SkipItems)
10644
return;
10645
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.");
10646
10647
// For reference, the old behavior was essentially:
10648
//window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
10649
}
10650
10651
static void ImGui::ErrorCheckNewFrameSanityChecks()
10652
{
10653
ImGuiContext& g = *GImGui;
10654
10655
// Check user IM_ASSERT macro
10656
// (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
10657
// If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
10658
// This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
10659
// #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
10660
// #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
10661
if (true) IM_ASSERT(1); else IM_ASSERT(0);
10662
10663
// Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
10664
// Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
10665
#ifdef __EMSCRIPTEN__
10666
if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
10667
g.IO.DeltaTime = 0.00001f;
10668
#endif
10669
10670
// Check user data
10671
// (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)
10672
IM_ASSERT(g.Initialized);
10673
IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
10674
IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
10675
IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
10676
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
10677
IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
10678
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
10679
IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting!");
10680
IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f && "Invalid style setting!"); // Required otherwise cannot resize from borders.
10681
IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
10682
IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
10683
IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes);
10684
10685
// Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
10686
if (g.IO.ConfigErrorRecovery)
10687
IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
10688
10689
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
10690
if (g.IO.FontGlobalScale > 1.0f)
10691
IM_ASSERT(g.Style.FontScaleMain == 1.0f && "Since 1.92: use style.FontScaleMain instead of g.IO.FontGlobalScale!");
10692
10693
// Remap legacy names
10694
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
10695
{
10696
g.IO.ConfigNavMoveSetMousePos = true;
10697
g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
10698
}
10699
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
10700
{
10701
g.IO.ConfigNavCaptureKeyboard = false;
10702
g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
10703
}
10704
10705
// Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
10706
if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
10707
g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
10708
if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
10709
g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };
10710
#endif
10711
}
10712
10713
static void ImGui::ErrorCheckEndFrameSanityChecks()
10714
{
10715
// Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
10716
// One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
10717
// It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
10718
// send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
10719
// We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
10720
// while still correctly asserting on mid-frame key press events.
10721
ImGuiContext& g = *GImGui;
10722
const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
10723
IM_UNUSED(g);
10724
IM_UNUSED(key_mods);
10725
IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
10726
IM_UNUSED(key_mods);
10727
10728
IM_ASSERT(g.CurrentWindowStack.Size == 1);
10729
IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
10730
}
10731
10732
// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
10733
void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
10734
{
10735
ImGuiContext& g = *GImGui;
10736
state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
10737
state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
10738
state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
10739
state_out->SizeOfColorStack = (short)g.ColorStack.Size;
10740
state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
10741
state_out->SizeOfFontStack = (short)g.FontStack.Size;
10742
state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
10743
state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
10744
state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
10745
state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
10746
state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
10747
}
10748
10749
// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
10750
// Called by e.g. EndFrame() but may be called for manual recovery.
10751
// Attempt to recover full window stack.
10752
void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
10753
{
10754
// PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
10755
ImGuiContext& g = *GImGui;
10756
while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
10757
{
10758
// Recap:
10759
// - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
10760
// - Always call a matching End() for each Begin() call, regardless of its return value!
10761
// - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
10762
// - We will fix that in a future major update.
10763
ImGuiWindow* window = g.CurrentWindow;
10764
if (window->Flags & ImGuiWindowFlags_ChildWindow)
10765
{
10766
if (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
10767
{
10768
IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
10769
EndTable();
10770
}
10771
else
10772
{
10773
IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
10774
EndChild();
10775
}
10776
}
10777
else
10778
{
10779
IM_ASSERT_USER_ERROR(0, "Missing End()");
10780
End();
10781
}
10782
}
10783
if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
10784
ErrorRecoveryTryToRecoverWindowState(state_in);
10785
}
10786
10787
// Called by e.g. End() but may be called for manual recovery.
10788
// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
10789
// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
10790
void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
10791
{
10792
ImGuiContext& g = *GImGui;
10793
10794
while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
10795
{
10796
IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
10797
EndTable();
10798
}
10799
10800
ImGuiWindow* window = g.CurrentWindow;
10801
10802
// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
10803
while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
10804
{
10805
IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
10806
EndTabBar();
10807
}
10808
while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
10809
{
10810
IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
10811
EndMultiSelect();
10812
}
10813
if (window->DC.MenuBarAppending) //-V1044
10814
{
10815
IM_ASSERT_USER_ERROR(0, "Missing EndMenuBar()");
10816
EndMenuBar();
10817
}
10818
while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
10819
{
10820
IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
10821
TreePop();
10822
}
10823
while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
10824
{
10825
IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
10826
EndGroup();
10827
}
10828
IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
10829
while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
10830
{
10831
IM_ASSERT_USER_ERROR(0, "Missing PopID()");
10832
PopID();
10833
}
10834
while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
10835
{
10836
IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
10837
if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10838
EndDisabled();
10839
else
10840
{
10841
EndDisabledOverrideReenable();
10842
g.CurrentWindowStack.back().DisabledOverrideReenable = false;
10843
}
10844
}
10845
IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
10846
while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
10847
{
10848
IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
10849
PopStyleColor();
10850
}
10851
while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
10852
{
10853
IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
10854
PopItemFlag();
10855
}
10856
while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
10857
{
10858
IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
10859
PopStyleVar();
10860
}
10861
while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
10862
{
10863
IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
10864
PopFont();
10865
}
10866
while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
10867
{
10868
IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
10869
PopFocusScope();
10870
}
10871
//IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
10872
}
10873
10874
bool ImGui::ErrorLog(const char* msg)
10875
{
10876
ImGuiContext& g = *GImGui;
10877
10878
// Output to debug log
10879
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10880
ImGuiWindow* window = g.CurrentWindow;
10881
10882
if (g.IO.ConfigErrorRecoveryEnableDebugLog)
10883
{
10884
if (g.ErrorFirst)
10885
IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
10886
g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
10887
IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
10888
}
10889
g.ErrorFirst = false;
10890
10891
// Output to tooltip
10892
if (g.IO.ConfigErrorRecoveryEnableTooltip)
10893
{
10894
if (g.WithinFrameScope && BeginErrorTooltip())
10895
{
10896
if (g.ErrorCountCurrentFrame < 20)
10897
{
10898
Text("In window '%s': %s", window ? window->Name : "NULL", msg);
10899
if (window && (!window->IsFallbackWindow || window->WasActive))
10900
GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
10901
}
10902
if (g.ErrorCountCurrentFrame == 20)
10903
Text("(and more errors)");
10904
// EndFrame() will amend debug buttons to this window, after all errors have been submitted.
10905
EndErrorTooltip();
10906
}
10907
g.ErrorCountCurrentFrame++;
10908
}
10909
#endif
10910
10911
// Output to callback
10912
if (g.ErrorCallback != NULL)
10913
g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
10914
10915
// Return whether we should assert
10916
return g.IO.ConfigErrorRecoveryEnableAssert;
10917
}
10918
10919
void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
10920
{
10921
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10922
ImGuiContext& g = *GImGui;
10923
if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false)
10924
g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
10925
if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
10926
{
10927
Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
10928
BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
10929
BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
10930
//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.
10931
BulletText("Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.");
10932
Separator();
10933
if (g.IO.ConfigDebugHighlightIdConflictsShowItemPicker)
10934
{
10935
Text("(Hold CTRL to: use ");
10936
SameLine(0.0f, 0.0f);
10937
if (SmallButton("Item Picker"))
10938
DebugStartItemPicker();
10939
SameLine(0.0f, 0.0f);
10940
Text(" to break in item call-stack, or ");
10941
}
10942
else
10943
{
10944
Text("(Hold CTRL to ");
10945
}
10946
SameLine(0.0f, 0.0f);
10947
if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
10948
g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
10949
SameLine(0.0f, 0.0f);
10950
Text(")");
10951
EndErrorTooltip();
10952
}
10953
10954
if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
10955
{
10956
Separator();
10957
Text("(Hold CTRL to:");
10958
SameLine();
10959
if (SmallButton("Enable Asserts"))
10960
g.IO.ConfigErrorRecoveryEnableAssert = true;
10961
//SameLine();
10962
//if (SmallButton("Hide Error Tooltips"))
10963
// g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
10964
SameLine(0, 0);
10965
Text(")");
10966
EndErrorTooltip();
10967
}
10968
#endif
10969
}
10970
10971
// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
10972
bool ImGui::BeginErrorTooltip()
10973
{
10974
ImGuiContext& g = *GImGui;
10975
ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
10976
const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
10977
PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
10978
if (use_locked_pos)
10979
SetNextWindowPos(g.ErrorTooltipLockedPos);
10980
bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
10981
PopStyleColor();
10982
if (is_visible && g.CurrentWindow->BeginCount == 1)
10983
{
10984
SeparatorText("MESSAGE FROM DEAR IMGUI");
10985
BringWindowToDisplayFront(g.CurrentWindow);
10986
BringWindowToFocusFront(g.CurrentWindow);
10987
g.ErrorTooltipLockedPos = GetWindowPos();
10988
}
10989
else if (!is_visible)
10990
{
10991
End();
10992
}
10993
return is_visible;
10994
}
10995
10996
void ImGui::EndErrorTooltip()
10997
{
10998
End();
10999
}
11000
11001
//-----------------------------------------------------------------------------
11002
// [SECTION] ITEM SUBMISSION
11003
//-----------------------------------------------------------------------------
11004
// - KeepAliveID()
11005
// - ItemAdd()
11006
//-----------------------------------------------------------------------------
11007
11008
// 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().
11009
void ImGui::KeepAliveID(ImGuiID id)
11010
{
11011
ImGuiContext& g = *GImGui;
11012
if (g.ActiveId == id)
11013
g.ActiveIdIsAlive = id;
11014
if (g.DeactivatedItemData.ID == id)
11015
g.DeactivatedItemData.IsAlive = true;
11016
}
11017
11018
// Declare item bounding box for clipping and interaction.
11019
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
11020
// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
11021
// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
11022
IM_MSVC_RUNTIME_CHECKS_OFF
11023
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
11024
{
11025
ImGuiContext& g = *GImGui;
11026
ImGuiWindow* window = g.CurrentWindow;
11027
11028
// Set item data
11029
// (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
11030
g.LastItemData.ID = id;
11031
g.LastItemData.Rect = bb;
11032
g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
11033
g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
11034
g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
11035
// Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
11036
11037
if (id != 0)
11038
{
11039
KeepAliveID(id);
11040
11041
// Directional navigation processing
11042
// Runs prior to clipping early-out
11043
// (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
11044
// (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
11045
// unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
11046
// thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
11047
// We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
11048
// to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
11049
// We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
11050
// If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
11051
if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
11052
{
11053
// FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
11054
window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
11055
if (g.NavId == id || g.NavAnyRequest)
11056
if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
11057
if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
11058
NavProcessItem();
11059
}
11060
11061
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
11062
ItemHandleShortcut(id);
11063
}
11064
11065
// Lightweight clear of SetNextItemXXX data.
11066
g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
11067
g.NextItemData.ItemFlags = ImGuiItemFlags_None;
11068
11069
#ifdef IMGUI_ENABLE_TEST_ENGINE
11070
if (id != 0)
11071
IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
11072
#endif
11073
11074
// Clipping test
11075
// (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')
11076
// g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
11077
const bool is_rect_visible = bb.Overlaps(window->ClipRect);
11078
if (!is_rect_visible)
11079
if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
11080
if (!g.ItemUnclipByLog)
11081
return false;
11082
11083
// [DEBUG]
11084
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11085
if (id != 0)
11086
{
11087
if (id == g.DebugLocateId)
11088
DebugLocateItemResolveWithLastItem();
11089
11090
// [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
11091
// Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
11092
// READ THE FAQ: https://dearimgui.com/faq
11093
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!");
11094
}
11095
//if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
11096
//if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
11097
// window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
11098
#endif
11099
11100
if (id != 0 && g.DeactivatedItemData.ID == id)
11101
g.DeactivatedItemData.ElapseFrame = g.FrameCount;
11102
11103
// We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
11104
if (is_rect_visible)
11105
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
11106
if (IsMouseHoveringRect(bb.Min, bb.Max))
11107
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
11108
return true;
11109
}
11110
IM_MSVC_RUNTIME_CHECKS_RESTORE
11111
11112
//-----------------------------------------------------------------------------
11113
// [SECTION] LAYOUT
11114
//-----------------------------------------------------------------------------
11115
// - ItemSize()
11116
// - SameLine()
11117
// - GetCursorScreenPos()
11118
// - SetCursorScreenPos()
11119
// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
11120
// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
11121
// - GetCursorStartPos()
11122
// - Indent()
11123
// - Unindent()
11124
// - SetNextItemWidth()
11125
// - PushItemWidth()
11126
// - PushMultiItemsWidths()
11127
// - PopItemWidth()
11128
// - CalcItemWidth()
11129
// - CalcItemSize()
11130
// - GetTextLineHeight()
11131
// - GetTextLineHeightWithSpacing()
11132
// - GetFrameHeight()
11133
// - GetFrameHeightWithSpacing()
11134
// - GetContentRegionMax()
11135
// - GetContentRegionAvail(),
11136
// - BeginGroup()
11137
// - EndGroup()
11138
// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
11139
//-----------------------------------------------------------------------------
11140
11141
// Advance cursor given item size for layout.
11142
// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
11143
// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
11144
// THIS IS IN THE PERFORMANCE CRITICAL PATH.
11145
IM_MSVC_RUNTIME_CHECKS_OFF
11146
void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
11147
{
11148
ImGuiContext& g = *GImGui;
11149
ImGuiWindow* window = g.CurrentWindow;
11150
if (window->SkipItems)
11151
return;
11152
11153
// We increase the height in this function to accommodate for baseline offset.
11154
// In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
11155
// but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
11156
const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
11157
11158
const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
11159
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);
11160
11161
// Always align ourselves on pixel boundaries
11162
//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]
11163
window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
11164
window->DC.CursorPosPrevLine.y = line_y1;
11165
window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
11166
window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line
11167
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
11168
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
11169
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11170
11171
window->DC.PrevLineSize.y = line_height;
11172
window->DC.CurrLineSize.y = 0.0f;
11173
window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
11174
window->DC.CurrLineTextBaseOffset = 0.0f;
11175
window->DC.IsSameLine = window->DC.IsSetPos = false;
11176
11177
// Horizontal layout mode
11178
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11179
SameLine();
11180
}
11181
IM_MSVC_RUNTIME_CHECKS_RESTORE
11182
11183
// Gets back to previous line and continue with horizontal layout
11184
// offset_from_start_x == 0 : follow right after previous item
11185
// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
11186
// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
11187
// spacing_w >= 0 : enforce spacing amount
11188
void ImGui::SameLine(float offset_from_start_x, float spacing_w)
11189
{
11190
ImGuiContext& g = *GImGui;
11191
ImGuiWindow* window = g.CurrentWindow;
11192
if (window->SkipItems)
11193
return;
11194
11195
if (offset_from_start_x != 0.0f)
11196
{
11197
if (spacing_w < 0.0f)
11198
spacing_w = 0.0f;
11199
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
11200
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11201
}
11202
else
11203
{
11204
if (spacing_w < 0.0f)
11205
spacing_w = g.Style.ItemSpacing.x;
11206
window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
11207
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11208
}
11209
window->DC.CurrLineSize = window->DC.PrevLineSize;
11210
window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11211
window->DC.IsSameLine = true;
11212
}
11213
11214
ImVec2 ImGui::GetCursorScreenPos()
11215
{
11216
ImGuiWindow* window = GetCurrentWindowRead();
11217
return window->DC.CursorPos;
11218
}
11219
11220
void ImGui::SetCursorScreenPos(const ImVec2& pos)
11221
{
11222
ImGuiWindow* window = GetCurrentWindow();
11223
window->DC.CursorPos = pos;
11224
//window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11225
window->DC.IsSetPos = true;
11226
}
11227
11228
// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
11229
// 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'.
11230
ImVec2 ImGui::GetCursorPos()
11231
{
11232
ImGuiWindow* window = GetCurrentWindowRead();
11233
return window->DC.CursorPos - window->Pos + window->Scroll;
11234
}
11235
11236
float ImGui::GetCursorPosX()
11237
{
11238
ImGuiWindow* window = GetCurrentWindowRead();
11239
return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
11240
}
11241
11242
float ImGui::GetCursorPosY()
11243
{
11244
ImGuiWindow* window = GetCurrentWindowRead();
11245
return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
11246
}
11247
11248
void ImGui::SetCursorPos(const ImVec2& local_pos)
11249
{
11250
ImGuiWindow* window = GetCurrentWindow();
11251
window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
11252
//window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11253
window->DC.IsSetPos = true;
11254
}
11255
11256
void ImGui::SetCursorPosX(float x)
11257
{
11258
ImGuiWindow* window = GetCurrentWindow();
11259
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
11260
//window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
11261
window->DC.IsSetPos = true;
11262
}
11263
11264
void ImGui::SetCursorPosY(float y)
11265
{
11266
ImGuiWindow* window = GetCurrentWindow();
11267
window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
11268
//window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
11269
window->DC.IsSetPos = true;
11270
}
11271
11272
ImVec2 ImGui::GetCursorStartPos()
11273
{
11274
ImGuiWindow* window = GetCurrentWindowRead();
11275
return window->DC.CursorStartPos - window->Pos;
11276
}
11277
11278
void ImGui::Indent(float indent_w)
11279
{
11280
ImGuiContext& g = *GImGui;
11281
ImGuiWindow* window = GetCurrentWindow();
11282
window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11283
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11284
}
11285
11286
void ImGui::Unindent(float indent_w)
11287
{
11288
ImGuiContext& g = *GImGui;
11289
ImGuiWindow* window = GetCurrentWindow();
11290
window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11291
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11292
}
11293
11294
// Affect large frame+labels widgets only.
11295
void ImGui::SetNextItemWidth(float item_width)
11296
{
11297
ImGuiContext& g = *GImGui;
11298
g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
11299
g.NextItemData.Width = item_width;
11300
}
11301
11302
// FIXME: Remove the == 0.0f behavior?
11303
void ImGui::PushItemWidth(float item_width)
11304
{
11305
ImGuiContext& g = *GImGui;
11306
ImGuiWindow* window = g.CurrentWindow;
11307
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11308
window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
11309
g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11310
}
11311
11312
void ImGui::PushMultiItemsWidths(int components, float w_full)
11313
{
11314
ImGuiContext& g = *GImGui;
11315
ImGuiWindow* window = g.CurrentWindow;
11316
IM_ASSERT(components > 0);
11317
const ImGuiStyle& style = g.Style;
11318
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11319
float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
11320
float prev_split = w_items;
11321
for (int i = components - 1; i > 0; i--)
11322
{
11323
float next_split = IM_TRUNC(w_items * i / components);
11324
window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
11325
prev_split = next_split;
11326
}
11327
window->DC.ItemWidth = ImMax(prev_split, 1.0f);
11328
g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11329
}
11330
11331
void ImGui::PopItemWidth()
11332
{
11333
ImGuiContext& g = *GImGui;
11334
ImGuiWindow* window = g.CurrentWindow;
11335
if (window->DC.ItemWidthStack.Size <= 0)
11336
{
11337
IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
11338
return;
11339
}
11340
window->DC.ItemWidth = window->DC.ItemWidthStack.back();
11341
window->DC.ItemWidthStack.pop_back();
11342
}
11343
11344
// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
11345
// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
11346
float ImGui::CalcItemWidth()
11347
{
11348
ImGuiContext& g = *GImGui;
11349
ImGuiWindow* window = g.CurrentWindow;
11350
float w;
11351
if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
11352
w = g.NextItemData.Width;
11353
else
11354
w = window->DC.ItemWidth;
11355
if (w < 0.0f)
11356
{
11357
float region_avail_x = GetContentRegionAvail().x;
11358
w = ImMax(1.0f, region_avail_x + w);
11359
}
11360
w = IM_TRUNC(w);
11361
return w;
11362
}
11363
11364
// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
11365
// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
11366
// Note that only CalcItemWidth() is publicly exposed.
11367
// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
11368
ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
11369
{
11370
ImVec2 avail;
11371
if (size.x < 0.0f || size.y < 0.0f)
11372
avail = GetContentRegionAvail();
11373
11374
if (size.x == 0.0f)
11375
size.x = default_w;
11376
else if (size.x < 0.0f)
11377
size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting
11378
11379
if (size.y == 0.0f)
11380
size.y = default_h;
11381
else if (size.y < 0.0f)
11382
size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting
11383
11384
return size;
11385
}
11386
11387
float ImGui::GetTextLineHeight()
11388
{
11389
ImGuiContext& g = *GImGui;
11390
return g.FontSize;
11391
}
11392
11393
float ImGui::GetTextLineHeightWithSpacing()
11394
{
11395
ImGuiContext& g = *GImGui;
11396
return g.FontSize + g.Style.ItemSpacing.y;
11397
}
11398
11399
float ImGui::GetFrameHeight()
11400
{
11401
ImGuiContext& g = *GImGui;
11402
return g.FontSize + g.Style.FramePadding.y * 2.0f;
11403
}
11404
11405
float ImGui::GetFrameHeightWithSpacing()
11406
{
11407
ImGuiContext& g = *GImGui;
11408
return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
11409
}
11410
11411
ImVec2 ImGui::GetContentRegionAvail()
11412
{
11413
ImGuiContext& g = *GImGui;
11414
ImGuiWindow* window = g.CurrentWindow;
11415
ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11416
return mx - window->DC.CursorPos;
11417
}
11418
11419
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11420
11421
// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
11422
// They are bizarre local-coordinates which don't play well with scrolling.
11423
ImVec2 ImGui::GetContentRegionMax()
11424
{
11425
return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
11426
}
11427
11428
ImVec2 ImGui::GetWindowContentRegionMin()
11429
{
11430
ImGuiWindow* window = GImGui->CurrentWindow;
11431
return window->ContentRegionRect.Min - window->Pos;
11432
}
11433
11434
ImVec2 ImGui::GetWindowContentRegionMax()
11435
{
11436
ImGuiWindow* window = GImGui->CurrentWindow;
11437
return window->ContentRegionRect.Max - window->Pos;
11438
}
11439
#endif
11440
11441
// 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.)
11442
// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
11443
// FIXME-OPT: Could we safely early out on ->SkipItems?
11444
void ImGui::BeginGroup()
11445
{
11446
ImGuiContext& g = *GImGui;
11447
ImGuiWindow* window = g.CurrentWindow;
11448
11449
g.GroupStack.resize(g.GroupStack.Size + 1);
11450
ImGuiGroupData& group_data = g.GroupStack.back();
11451
group_data.WindowID = window->ID;
11452
group_data.BackupCursorPos = window->DC.CursorPos;
11453
group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
11454
group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
11455
group_data.BackupIndent = window->DC.Indent;
11456
group_data.BackupGroupOffset = window->DC.GroupOffset;
11457
group_data.BackupCurrLineSize = window->DC.CurrLineSize;
11458
group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
11459
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
11460
group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
11461
group_data.BackupIsSameLine = window->DC.IsSameLine;
11462
group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive;
11463
group_data.EmitItem = true;
11464
11465
window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
11466
window->DC.Indent = window->DC.GroupOffset;
11467
window->DC.CursorMaxPos = window->DC.CursorPos;
11468
window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
11469
if (g.LogEnabled)
11470
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11471
}
11472
11473
void ImGui::EndGroup()
11474
{
11475
ImGuiContext& g = *GImGui;
11476
ImGuiWindow* window = g.CurrentWindow;
11477
IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
11478
11479
ImGuiGroupData& group_data = g.GroupStack.back();
11480
IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
11481
11482
if (window->DC.IsSetPos)
11483
ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
11484
11485
// Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
11486
ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));
11487
window->DC.CursorPos = group_data.BackupCursorPos;
11488
window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
11489
window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);
11490
window->DC.Indent = group_data.BackupIndent;
11491
window->DC.GroupOffset = group_data.BackupGroupOffset;
11492
window->DC.CurrLineSize = group_data.BackupCurrLineSize;
11493
window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
11494
window->DC.IsSameLine = group_data.BackupIsSameLine;
11495
if (g.LogEnabled)
11496
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11497
11498
if (!group_data.EmitItem)
11499
{
11500
g.GroupStack.pop_back();
11501
return;
11502
}
11503
11504
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.
11505
ItemSize(group_bb.GetSize());
11506
ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
11507
11508
// 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.
11509
// 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.
11510
// Also if you grep for LastItemId you'll notice it is only used in that context.
11511
// (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.)
11512
const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
11513
const bool group_contains_deactivated_id = (group_data.BackupDeactivatedIdIsAlive == false) && (g.DeactivatedItemData.IsAlive == true);
11514
if (group_contains_curr_active_id)
11515
g.LastItemData.ID = g.ActiveId;
11516
else if (group_contains_deactivated_id)
11517
g.LastItemData.ID = g.DeactivatedItemData.ID;
11518
g.LastItemData.Rect = group_bb;
11519
11520
// Forward Hovered flag
11521
const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
11522
if (group_contains_curr_hovered_id)
11523
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
11524
11525
// Forward Edited flag
11526
if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
11527
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
11528
11529
// Forward Deactivated flag
11530
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
11531
if (group_contains_deactivated_id)
11532
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
11533
11534
g.GroupStack.pop_back();
11535
if (g.DebugShowGroupRects)
11536
window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
11537
}
11538
11539
11540
//-----------------------------------------------------------------------------
11541
// [SECTION] SCROLLING
11542
//-----------------------------------------------------------------------------
11543
11544
// Helper to snap on edges when aiming at an item very close to the edge,
11545
// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
11546
// When we refactor the scrolling API this may be configurable with a flag?
11547
// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
11548
static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
11549
{
11550
if (target <= snap_min + snap_threshold)
11551
return ImLerp(snap_min, target, center_ratio);
11552
if (target >= snap_max - snap_threshold)
11553
return ImLerp(target, snap_max, center_ratio);
11554
return target;
11555
}
11556
11557
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
11558
{
11559
ImVec2 scroll = window->Scroll;
11560
ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
11561
for (int axis = 0; axis < 2; axis++)
11562
{
11563
if (window->ScrollTarget[axis] < FLT_MAX)
11564
{
11565
float center_ratio = window->ScrollTargetCenterRatio[axis];
11566
float scroll_target = window->ScrollTarget[axis];
11567
if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
11568
{
11569
float snap_min = 0.0f;
11570
float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
11571
scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
11572
}
11573
window->ScrollExpected[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
11574
}
11575
11576
// Based on https://github.com/ocornut/imgui/pull/7348
11577
// Instead use "Scroll" value in the window, all setters that sets the scroll absolutely now points to
11578
// "ScrollExpected" Here, we take from ScrollTarget (from some functions like ScrollHere + mouse wheel) to set
11579
// the ScrollExpected value Also, Scroll var in window is processed to meet ScrollExpected Value
11580
//
11581
// The formula is pretty simple to generate a smooth scrolling that can be tweaked just by one float value.
11582
//
11583
// The Float is "ImGuiStyleVar_ScrollSmooth". Can be set on the style or via PushStyleVar.
11584
// A Value of 1.0f is just inmediate (transported from ScrollExpected to Scroll).
11585
// A Value higher of 1.0f will make the scrolling smoother.
11586
//
11587
// The ScrollExpected is also clamped (as previously the "Scroll" value) from 0 to sScrollMax
11588
//
11589
// The approach is frame bounded and not time bounded.
11590
// It should be prefereable use a time bounded approach but this is pretty simple so we don't need to add extra
11591
// vars to save a scrolling "start" time to have a delta / deal with posible increments during the scrolling
11592
// itself (restar timer) Anyway it should not be complicated to add but this approach is small, simple, can be
11593
// user or not and works pretty well
11594
//
11595
window->ScrollExpected[axis] = ImRound64(ImMax(window->ScrollExpected[axis], 0.0f));
11596
if (!window->Collapsed && !window->SkipItems)
11597
window->ScrollExpected[axis] = ImMin(window->ScrollExpected[axis], window->ScrollMax[axis]);
11598
ImGuiContext& g = *GImGui;
11599
ImGuiStyle& style = g.Style;
11600
if (scroll[axis] != window->ScrollExpected[axis])
11601
{
11602
const float multiplier = GImGui->IO.DeltaTime / (1.0f / ImMax(GImGui->IO.Framerate, 1.0f));
11603
const float diff = window->ScrollExpected[axis] - scroll[axis];
11604
if (diff > 0)
11605
scroll[axis] += ImMin(diff, (diff / (style.ScrollSmooth * multiplier)));
11606
else
11607
scroll[axis] -= ImMin(-diff, (-diff / (style.ScrollSmooth * multiplier)));
11608
11609
scroll[axis] = (window->Appearing || g.ScrollbarHeld & 1) ? window->ScrollExpected[axis] : scroll[axis];
11610
}
11611
}
11612
return scroll;
11613
}
11614
11615
void ImGui::ScrollToItem(ImGuiScrollFlags flags)
11616
{
11617
ImGuiContext& g = *GImGui;
11618
ImGuiWindow* window = g.CurrentWindow;
11619
ScrollToRectEx(window, g.LastItemData.NavRect, flags);
11620
}
11621
11622
void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11623
{
11624
ScrollToRectEx(window, item_rect, flags);
11625
}
11626
11627
// Scroll to keep newly navigated item fully into view
11628
ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11629
{
11630
ImGuiContext& g = *GImGui;
11631
ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
11632
scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
11633
scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
11634
//GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
11635
//GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
11636
11637
// Check that only one behavior is selected per axis
11638
IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
11639
IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
11640
11641
// Defaults
11642
ImGuiScrollFlags in_flags = flags;
11643
if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
11644
flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
11645
if ((flags & ImGuiScrollFlags_MaskY_) == 0)
11646
flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
11647
11648
const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
11649
const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
11650
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;
11651
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;
11652
11653
if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
11654
{
11655
if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
11656
SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
11657
else if (item_rect.Max.x >= scroll_rect.Max.x)
11658
SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
11659
}
11660
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
11661
{
11662
if (can_be_fully_visible_x)
11663
SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
11664
else
11665
SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
11666
}
11667
11668
if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
11669
{
11670
if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
11671
SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
11672
else if (item_rect.Max.y >= scroll_rect.Max.y)
11673
SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
11674
}
11675
else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
11676
{
11677
if (can_be_fully_visible_y)
11678
SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
11679
else
11680
SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
11681
}
11682
11683
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
11684
ImVec2 delta_scroll = next_scroll - window->Scroll;
11685
11686
// Also scroll parent window to keep us into view if necessary
11687
if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
11688
{
11689
// FIXME-SCROLL: May be an option?
11690
if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
11691
in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
11692
if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
11693
in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
11694
delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
11695
}
11696
11697
return delta_scroll;
11698
}
11699
11700
float ImGui::GetScrollX()
11701
{
11702
ImGuiWindow* window = GImGui->CurrentWindow;
11703
return window->ScrollExpected.x;
11704
}
11705
11706
float ImGui::GetScrollY()
11707
{
11708
ImGuiWindow* window = GImGui->CurrentWindow;
11709
return window->ScrollExpected.y;
11710
}
11711
11712
float ImGui::GetScrollMaxX()
11713
{
11714
ImGuiWindow* window = GImGui->CurrentWindow;
11715
return window->ScrollMax.x;
11716
}
11717
11718
float ImGui::GetScrollMaxY()
11719
{
11720
ImGuiWindow* window = GImGui->CurrentWindow;
11721
return window->ScrollMax.y;
11722
}
11723
11724
void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
11725
{
11726
window->ScrollTarget.x = scroll_x;
11727
window->ScrollTargetCenterRatio.x = 0.0f;
11728
window->ScrollTargetEdgeSnapDist.x = 0.0f;
11729
}
11730
11731
void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
11732
{
11733
window->ScrollTarget.y = scroll_y;
11734
window->ScrollTargetCenterRatio.y = 0.0f;
11735
window->ScrollTargetEdgeSnapDist.y = 0.0f;
11736
}
11737
11738
void ImGui::SetScrollX(float scroll_x)
11739
{
11740
ImGuiContext& g = *GImGui;
11741
SetScrollX(g.CurrentWindow, scroll_x);
11742
}
11743
11744
void ImGui::SetScrollY(float scroll_y)
11745
{
11746
ImGuiContext& g = *GImGui;
11747
SetScrollY(g.CurrentWindow, scroll_y);
11748
}
11749
11750
// Note that a local position will vary depending on initial scroll value,
11751
// This is a little bit confusing so bear with us:
11752
// - local_pos = (absolution_pos - window->Pos)
11753
// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
11754
// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
11755
// - They mostly exist because of legacy API.
11756
// Following the rules above, when trying to work with scrolling code, consider that:
11757
// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
11758
// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
11759
// 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
11760
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
11761
{
11762
IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
11763
window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->ScrollExpected.x); // Convert local position to scroll offset
11764
window->ScrollTargetCenterRatio.x = center_x_ratio;
11765
window->ScrollTargetEdgeSnapDist.x = 0.0f;
11766
}
11767
11768
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
11769
{
11770
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
11771
window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->ScrollExpected.y); // Convert local position to scroll offset
11772
window->ScrollTargetCenterRatio.y = center_y_ratio;
11773
window->ScrollTargetEdgeSnapDist.y = 0.0f;
11774
}
11775
11776
void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
11777
{
11778
ImGuiContext& g = *GImGui;
11779
SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
11780
}
11781
11782
void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
11783
{
11784
ImGuiContext& g = *GImGui;
11785
SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
11786
}
11787
11788
// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
11789
void ImGui::SetScrollHereX(float center_x_ratio)
11790
{
11791
ImGuiContext& g = *GImGui;
11792
ImGuiWindow* window = g.CurrentWindow;
11793
float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
11794
float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
11795
SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
11796
11797
// Tweak: snap on edges when aiming at an item very close to the edge
11798
window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
11799
}
11800
11801
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
11802
void ImGui::SetScrollHereY(float center_y_ratio)
11803
{
11804
ImGuiContext& g = *GImGui;
11805
ImGuiWindow* window = g.CurrentWindow;
11806
float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
11807
float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
11808
SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
11809
11810
// Tweak: snap on edges when aiming at an item very close to the edge
11811
window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
11812
}
11813
11814
//-----------------------------------------------------------------------------
11815
// [SECTION] TOOLTIPS
11816
//-----------------------------------------------------------------------------
11817
11818
bool ImGui::BeginTooltip()
11819
{
11820
return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
11821
}
11822
11823
bool ImGui::BeginItemTooltip()
11824
{
11825
if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11826
return false;
11827
return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
11828
}
11829
11830
bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
11831
{
11832
ImGuiContext& g = *GImGui;
11833
11834
const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
11835
if (is_dragdrop_tooltip)
11836
{
11837
// Drag and Drop tooltips are positioning differently than other tooltips:
11838
// - offset visibility to increase visibility around mouse.
11839
// - never clamp within outer viewport boundary.
11840
// We call SetNextWindowPos() to enforce position and disable clamping.
11841
// See FindBestWindowPosForPopup() for positioning logic of other tooltips (not drag and drop ones).
11842
//ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
11843
const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
11844
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)
11845
{
11846
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);
11847
ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
11848
SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);
11849
}
11850
11851
SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
11852
//PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
11853
tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
11854
}
11855
11856
const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
11857
char window_name[32];
11858
ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount);
11859
if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active)
11860
{
11861
// Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
11862
//IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
11863
SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
11864
ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount);
11865
}
11866
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
11867
Begin(window_name, NULL, flags | extra_window_flags);
11868
// 2023-03-09: Added bool return value to the API, but currently always returning true.
11869
// If this ever returns false we need to update BeginDragDropSource() accordingly.
11870
//if (!ret)
11871
// End();
11872
//return ret;
11873
return true;
11874
}
11875
11876
void ImGui::EndTooltip()
11877
{
11878
IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
11879
End();
11880
}
11881
11882
void ImGui::SetTooltip(const char* fmt, ...)
11883
{
11884
va_list args;
11885
va_start(args, fmt);
11886
SetTooltipV(fmt, args);
11887
va_end(args);
11888
}
11889
11890
void ImGui::SetTooltipV(const char* fmt, va_list args)
11891
{
11892
if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
11893
return;
11894
TextV(fmt, args);
11895
EndTooltip();
11896
}
11897
11898
// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
11899
// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
11900
void ImGui::SetItemTooltip(const char* fmt, ...)
11901
{
11902
va_list args;
11903
va_start(args, fmt);
11904
if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11905
SetTooltipV(fmt, args);
11906
va_end(args);
11907
}
11908
11909
void ImGui::SetItemTooltipV(const char* fmt, va_list args)
11910
{
11911
if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11912
SetTooltipV(fmt, args);
11913
}
11914
11915
11916
//-----------------------------------------------------------------------------
11917
// [SECTION] POPUPS
11918
//-----------------------------------------------------------------------------
11919
11920
// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
11921
bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
11922
{
11923
ImGuiContext& g = *GImGui;
11924
if (popup_flags & ImGuiPopupFlags_AnyPopupId)
11925
{
11926
// Return true if any popup is open at the current BeginPopup() level of the popup stack
11927
// This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
11928
IM_ASSERT(id == 0);
11929
if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
11930
return g.OpenPopupStack.Size > 0;
11931
else
11932
return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
11933
}
11934
else
11935
{
11936
if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
11937
{
11938
// Return true if the popup is open anywhere in the popup stack
11939
for (int n = 0; n < g.OpenPopupStack.Size; n++)
11940
if (g.OpenPopupStack[n].PopupId == id)
11941
return true;
11942
return false;
11943
}
11944
else
11945
{
11946
// Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
11947
return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
11948
}
11949
}
11950
}
11951
11952
bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
11953
{
11954
ImGuiContext& g = *GImGui;
11955
ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
11956
if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
11957
IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
11958
return IsPopupOpen(id, popup_flags);
11959
}
11960
11961
// Also see FindBlockingModal(NULL)
11962
ImGuiWindow* ImGui::GetTopMostPopupModal()
11963
{
11964
ImGuiContext& g = *GImGui;
11965
for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
11966
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
11967
if (popup->Flags & ImGuiWindowFlags_Modal)
11968
return popup;
11969
return NULL;
11970
}
11971
11972
// See Demo->Stacked Modal to confirm what this is for.
11973
ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
11974
{
11975
ImGuiContext& g = *GImGui;
11976
for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
11977
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
11978
if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
11979
return popup;
11980
return NULL;
11981
}
11982
11983
11984
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
11985
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
11986
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
11987
// - WindowA // FindBlockingModal() returns Modal1
11988
// - WindowB // .. returns Modal1
11989
// - Modal1 // .. returns Modal2
11990
// - WindowC // .. returns Modal2
11991
// - WindowD // .. returns Modal2
11992
// - Modal2 // .. returns Modal2
11993
// - WindowE // .. returns NULL
11994
// Notes:
11995
// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
11996
// Only difference is here we check for ->Active/WasActive but it may be unnecessary.
11997
ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
11998
{
11999
ImGuiContext& g = *GImGui;
12000
if (g.OpenPopupStack.Size <= 0)
12001
return NULL;
12002
12003
// Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
12004
for (ImGuiPopupData& popup_data : g.OpenPopupStack)
12005
{
12006
ImGuiWindow* popup_window = popup_data.Window;
12007
if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
12008
continue;
12009
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.
12010
continue;
12011
if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
12012
return popup_window;
12013
if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal
12014
continue;
12015
return popup_window; // Place window right below first block modal
12016
}
12017
return NULL;
12018
}
12019
12020
void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
12021
{
12022
ImGuiContext& g = *GImGui;
12023
ImGuiID id = g.CurrentWindow->GetID(str_id);
12024
IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
12025
OpenPopupEx(id, popup_flags);
12026
}
12027
12028
void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
12029
{
12030
OpenPopupEx(id, popup_flags);
12031
}
12032
12033
// Mark popup as open (toggle toward open state).
12034
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
12035
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
12036
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
12037
void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
12038
{
12039
ImGuiContext& g = *GImGui;
12040
ImGuiWindow* parent_window = g.CurrentWindow;
12041
const int current_stack_size = g.BeginPopupStack.Size;
12042
12043
if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
12044
if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
12045
return;
12046
12047
ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
12048
popup_ref.PopupId = id;
12049
popup_ref.Window = NULL;
12050
popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type).
12051
popup_ref.OpenFrameCount = g.FrameCount;
12052
popup_ref.OpenParentId = parent_window->IDStack.back();
12053
popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
12054
popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
12055
12056
IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
12057
if (g.OpenPopupStack.Size < current_stack_size + 1)
12058
{
12059
g.OpenPopupStack.push_back(popup_ref);
12060
}
12061
else
12062
{
12063
// Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
12064
// However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
12065
// in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
12066
// Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
12067
bool keep_existing = false;
12068
if (g.OpenPopupStack[current_stack_size].PopupId == id)
12069
if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
12070
keep_existing = true;
12071
if (keep_existing)
12072
{
12073
// No reopen
12074
g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
12075
}
12076
else
12077
{
12078
// Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
12079
ClosePopupToLevel(current_stack_size, true);
12080
g.OpenPopupStack.push_back(popup_ref);
12081
}
12082
12083
// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
12084
// This is equivalent to what ClosePopupToLevel() does.
12085
//if (g.OpenPopupStack[current_stack_size].PopupId == id)
12086
// FocusWindow(parent_window);
12087
}
12088
}
12089
12090
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
12091
// This function closes any popups that are over 'ref_window'.
12092
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
12093
{
12094
ImGuiContext& g = *GImGui;
12095
if (g.OpenPopupStack.Size == 0)
12096
return;
12097
12098
// Don't close our own child popup windows.
12099
//IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
12100
int popup_count_to_keep = 0;
12101
if (ref_window)
12102
{
12103
// Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
12104
for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
12105
{
12106
ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
12107
if (!popup.Window)
12108
continue;
12109
IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
12110
12111
// Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
12112
// - Clicking/Focusing Window2 won't close Popup1:
12113
// Window -> Popup1 -> Window2(Ref)
12114
// - Clicking/focusing Popup1 will close Popup2 and Popup3:
12115
// Window -> Popup1(Ref) -> Popup2 -> Popup3
12116
// - Each popups may contain child windows, which is why we compare ->RootWindow!
12117
// Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
12118
// We step through every popup from bottom to top to validate their position relative to reference window.
12119
bool ref_window_is_descendent_of_popup = false;
12120
for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
12121
if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
12122
if (IsWindowWithinBeginStackOf(ref_window, popup_window))
12123
{
12124
ref_window_is_descendent_of_popup = true;
12125
break;
12126
}
12127
if (!ref_window_is_descendent_of_popup)
12128
break;
12129
}
12130
}
12131
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
12132
{
12133
IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
12134
ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
12135
}
12136
}
12137
12138
void ImGui::ClosePopupsExceptModals()
12139
{
12140
ImGuiContext& g = *GImGui;
12141
12142
int popup_count_to_keep;
12143
for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
12144
{
12145
ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
12146
if (!window || (window->Flags & ImGuiWindowFlags_Modal))
12147
break;
12148
}
12149
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
12150
ClosePopupToLevel(popup_count_to_keep, true);
12151
}
12152
12153
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
12154
{
12155
ImGuiContext& g = *GImGui;
12156
IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
12157
IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
12158
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)
12159
for (int n = remaining; n < g.OpenPopupStack.Size; n++)
12160
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);
12161
12162
// Trim open popup stack
12163
ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
12164
g.OpenPopupStack.resize(remaining);
12165
12166
// 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)
12167
if (restore_focus_to_window_under_popup && prev_popup.Window)
12168
{
12169
ImGuiWindow* popup_window = prev_popup.Window;
12170
ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
12171
if (focus_window && !focus_window->WasActive)
12172
FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
12173
else
12174
FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
12175
}
12176
}
12177
12178
// Close the popup we have begin-ed into.
12179
void ImGui::CloseCurrentPopup()
12180
{
12181
ImGuiContext& g = *GImGui;
12182
int popup_idx = g.BeginPopupStack.Size - 1;
12183
if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
12184
return;
12185
12186
// Closing a menu closes its top-most parent popup (unless a modal)
12187
while (popup_idx > 0)
12188
{
12189
ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
12190
ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
12191
bool close_parent = false;
12192
if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
12193
if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
12194
close_parent = true;
12195
if (!close_parent)
12196
break;
12197
popup_idx--;
12198
}
12199
IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
12200
ClosePopupToLevel(popup_idx, true);
12201
12202
// A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
12203
// To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
12204
// Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
12205
if (ImGuiWindow* window = g.NavWindow)
12206
window->DC.NavHideHighlightOneFrame = true;
12207
}
12208
12209
// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
12210
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
12211
{
12212
ImGuiContext& g = *GImGui;
12213
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12214
{
12215
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12216
return false;
12217
}
12218
12219
char name[20];
12220
IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx()
12221
ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame
12222
12223
bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
12224
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12225
EndPopup();
12226
//g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12227
return is_open;
12228
}
12229
12230
bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags)
12231
{
12232
ImGuiContext& g = *GImGui;
12233
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12234
{
12235
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12236
return false;
12237
}
12238
12239
char name[128];
12240
IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);
12241
ImFormatString(name, IM_ARRAYSIZE(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth
12242
bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
12243
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12244
EndPopup();
12245
//g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12246
return is_open;
12247
}
12248
12249
bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
12250
{
12251
ImGuiContext& g = *GImGui;
12252
if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
12253
{
12254
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12255
return false;
12256
}
12257
flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
12258
ImGuiID id = g.CurrentWindow->GetID(str_id);
12259
return BeginPopupEx(id, flags);
12260
}
12261
12262
// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
12263
// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
12264
// - *p_open set back to false in BeginPopupModal() when popup is not open.
12265
// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
12266
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
12267
{
12268
ImGuiContext& g = *GImGui;
12269
ImGuiWindow* window = g.CurrentWindow;
12270
const ImGuiID id = window->GetID(name);
12271
if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12272
{
12273
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12274
if (p_open && *p_open)
12275
*p_open = false;
12276
return false;
12277
}
12278
12279
// Center modal windows by default for increased visibility
12280
// (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
12281
// FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
12282
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)
12283
{
12284
const ImGuiViewport* viewport = GetMainViewport();
12285
SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
12286
}
12287
12288
flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
12289
const bool is_open = Begin(name, p_open, flags);
12290
if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
12291
{
12292
EndPopup();
12293
if (is_open)
12294
ClosePopupToLevel(g.BeginPopupStack.Size, true);
12295
return false;
12296
}
12297
return is_open;
12298
}
12299
12300
void ImGui::EndPopup()
12301
{
12302
ImGuiContext& g = *GImGui;
12303
ImGuiWindow* window = g.CurrentWindow;
12304
if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || g.BeginPopupStack.Size == 0)
12305
{
12306
IM_ASSERT_USER_ERROR(0, "Calling EndPopup() too many times or in wrong window!");
12307
return;
12308
}
12309
12310
// 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)
12311
if (g.NavWindow == window)
12312
NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
12313
12314
// Child-popups don't need to be laid out
12315
const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
12316
if (window->Flags & ImGuiWindowFlags_ChildWindow)
12317
g.WithinEndChildID = window->ID;
12318
End();
12319
g.WithinEndChildID = backup_within_end_child_id;
12320
}
12321
12322
// Helper to open a popup if mouse button is released over the item
12323
// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
12324
void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
12325
{
12326
ImGuiContext& g = *GImGui;
12327
ImGuiWindow* window = g.CurrentWindow;
12328
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12329
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12330
{
12331
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!
12332
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12333
OpenPopupEx(id, popup_flags);
12334
}
12335
}
12336
12337
// This is a helper to handle the simplest case of associating one named popup to one given widget.
12338
// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
12339
// - To create a popup with a specific identifier, pass it in str_id.
12340
// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
12341
// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
12342
// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
12343
// This is essentially the same as:
12344
// id = str_id ? GetID(str_id) : GetItemID();
12345
// OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
12346
// return BeginPopup(id);
12347
// Which is essentially the same as:
12348
// id = str_id ? GetID(str_id) : GetItemID();
12349
// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
12350
// OpenPopup(id);
12351
// return BeginPopup(id);
12352
// The main difference being that this is tweaked to avoid computing the ID twice.
12353
bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
12354
{
12355
ImGuiContext& g = *GImGui;
12356
ImGuiWindow* window = g.CurrentWindow;
12357
if (window->SkipItems)
12358
return false;
12359
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!
12360
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12361
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12362
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12363
OpenPopupEx(id, popup_flags);
12364
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12365
}
12366
12367
bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
12368
{
12369
ImGuiContext& g = *GImGui;
12370
ImGuiWindow* window = g.CurrentWindow;
12371
if (!str_id)
12372
str_id = "window_context";
12373
ImGuiID id = window->GetID(str_id);
12374
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12375
if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12376
if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
12377
OpenPopupEx(id, popup_flags);
12378
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12379
}
12380
12381
bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
12382
{
12383
ImGuiContext& g = *GImGui;
12384
ImGuiWindow* window = g.CurrentWindow;
12385
if (!str_id)
12386
str_id = "void_context";
12387
ImGuiID id = window->GetID(str_id);
12388
int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12389
if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
12390
if (GetTopMostPopupModal() == NULL)
12391
OpenPopupEx(id, popup_flags);
12392
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12393
}
12394
12395
// 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.)
12396
// 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.
12397
// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
12398
// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
12399
// this allows us to have tooltips/popups displayed out of the parent viewport.)
12400
ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
12401
{
12402
ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
12403
//GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
12404
//GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
12405
12406
// Combo Box policy (we want a connecting edge)
12407
if (policy == ImGuiPopupPositionPolicy_ComboBox)
12408
{
12409
const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
12410
for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12411
{
12412
const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12413
if (n != -1 && dir == *last_dir) // Already tried this direction?
12414
continue;
12415
ImVec2 pos;
12416
if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
12417
if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
12418
if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
12419
if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
12420
if (!r_outer.Contains(ImRect(pos, pos + size)))
12421
continue;
12422
*last_dir = dir;
12423
return pos;
12424
}
12425
}
12426
12427
// Tooltip and Default popup policy
12428
// (Always first try the direction we used on the last frame, if any)
12429
if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
12430
{
12431
const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
12432
for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12433
{
12434
const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12435
if (n != -1 && dir == *last_dir) // Already tried this direction?
12436
continue;
12437
12438
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);
12439
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);
12440
12441
// 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)
12442
if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
12443
continue;
12444
if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
12445
continue;
12446
12447
ImVec2 pos;
12448
pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
12449
pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
12450
12451
// Clamp top-left corner of popup
12452
pos.x = ImMax(pos.x, r_outer.Min.x);
12453
pos.y = ImMax(pos.y, r_outer.Min.y);
12454
12455
*last_dir = dir;
12456
return pos;
12457
}
12458
}
12459
12460
// Fallback when not enough room:
12461
*last_dir = ImGuiDir_None;
12462
12463
// For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
12464
if (policy == ImGuiPopupPositionPolicy_Tooltip)
12465
return ref_pos + ImVec2(2, 2);
12466
12467
// Otherwise try to keep within display
12468
ImVec2 pos = ref_pos;
12469
pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
12470
pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
12471
return pos;
12472
}
12473
12474
// Note that this is used for popups, which can overlap the non work-area of individual viewports.
12475
ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
12476
{
12477
ImGuiContext& g = *GImGui;
12478
IM_UNUSED(window);
12479
ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
12480
ImVec2 padding = g.Style.DisplaySafeAreaPadding;
12481
r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
12482
return r_screen;
12483
}
12484
12485
ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
12486
{
12487
ImGuiContext& g = *GImGui;
12488
12489
ImRect r_outer = GetPopupAllowedExtentRect(window);
12490
if (window->Flags & ImGuiWindowFlags_ChildMenu)
12491
{
12492
// Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
12493
// This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
12494
IM_ASSERT(g.CurrentWindow == window);
12495
ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
12496
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).
12497
ImRect r_avoid;
12498
if (parent_window->DC.MenuBarAppending)
12499
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
12500
else
12501
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);
12502
return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
12503
}
12504
if (window->Flags & ImGuiWindowFlags_Popup)
12505
{
12506
return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
12507
}
12508
if (window->Flags & ImGuiWindowFlags_Tooltip)
12509
{
12510
// Position tooltip (always follows mouse + clamp within outer boundaries)
12511
// FIXME:
12512
// - 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).
12513
// - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
12514
// - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
12515
// as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
12516
IM_ASSERT(g.CurrentWindow == window);
12517
const float scale = g.Style.MouseCursorScale;
12518
const ImVec2 ref_pos = NavCalcPreferredRefPos();
12519
12520
if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
12521
{
12522
ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
12523
if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
12524
return tooltip_pos;
12525
}
12526
12527
ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
12528
ImRect r_avoid;
12529
if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
12530
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
12531
else
12532
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.
12533
//GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
12534
12535
return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
12536
}
12537
IM_ASSERT(0);
12538
return window->Pos;
12539
}
12540
12541
//-----------------------------------------------------------------------------
12542
// [SECTION] WINDOW FOCUS
12543
//----------------------------------------------------------------------------
12544
// - SetWindowFocus()
12545
// - SetNextWindowFocus()
12546
// - IsWindowFocused()
12547
// - UpdateWindowInFocusOrderList() [Internal]
12548
// - BringWindowToFocusFront() [Internal]
12549
// - BringWindowToDisplayFront() [Internal]
12550
// - BringWindowToDisplayBack() [Internal]
12551
// - BringWindowToDisplayBehind() [Internal]
12552
// - FindWindowDisplayIndex() [Internal]
12553
// - FocusWindow() [Internal]
12554
// - FocusTopMostWindowUnderOne() [Internal]
12555
//-----------------------------------------------------------------------------
12556
12557
void ImGui::SetWindowFocus()
12558
{
12559
FocusWindow(GImGui->CurrentWindow);
12560
}
12561
12562
void ImGui::SetWindowFocus(const char* name)
12563
{
12564
if (name)
12565
{
12566
if (ImGuiWindow* window = FindWindowByName(name))
12567
FocusWindow(window);
12568
}
12569
else
12570
{
12571
FocusWindow(NULL);
12572
}
12573
}
12574
12575
void ImGui::SetNextWindowFocus()
12576
{
12577
ImGuiContext& g = *GImGui;
12578
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasFocus;
12579
}
12580
12581
// Similar to IsWindowHovered()
12582
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
12583
{
12584
ImGuiContext& g = *GImGui;
12585
ImGuiWindow* ref_window = g.NavWindow;
12586
ImGuiWindow* cur_window = g.CurrentWindow;
12587
12588
if (ref_window == NULL)
12589
return false;
12590
if (flags & ImGuiFocusedFlags_AnyWindow)
12591
return true;
12592
12593
IM_ASSERT(cur_window); // Not inside a Begin()/End()
12594
const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
12595
if (flags & ImGuiHoveredFlags_RootWindow)
12596
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
12597
12598
if (flags & ImGuiHoveredFlags_ChildWindows)
12599
return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
12600
else
12601
return (ref_window == cur_window);
12602
}
12603
12604
static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
12605
{
12606
ImGuiContext& g = *GImGui;
12607
IM_UNUSED(g);
12608
int order = window->FocusOrder;
12609
IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
12610
IM_ASSERT(g.WindowsFocusOrder[order] == window);
12611
return order;
12612
}
12613
12614
static void ImGui::UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
12615
{
12616
ImGuiContext& g = *GImGui;
12617
12618
const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
12619
const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
12620
if ((just_created || child_flag_changed) && !new_is_explicit_child)
12621
{
12622
IM_ASSERT(!g.WindowsFocusOrder.contains(window));
12623
g.WindowsFocusOrder.push_back(window);
12624
window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
12625
}
12626
else if (!just_created && child_flag_changed && new_is_explicit_child)
12627
{
12628
IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
12629
for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
12630
g.WindowsFocusOrder[n]->FocusOrder--;
12631
g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
12632
window->FocusOrder = -1;
12633
}
12634
window->IsExplicitChild = new_is_explicit_child;
12635
}
12636
12637
void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
12638
{
12639
ImGuiContext& g = *GImGui;
12640
IM_ASSERT(window == window->RootWindow);
12641
12642
const int cur_order = window->FocusOrder;
12643
IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
12644
if (g.WindowsFocusOrder.back() == window)
12645
return;
12646
12647
const int new_order = g.WindowsFocusOrder.Size - 1;
12648
for (int n = cur_order; n < new_order; n++)
12649
{
12650
g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
12651
g.WindowsFocusOrder[n]->FocusOrder--;
12652
IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
12653
}
12654
g.WindowsFocusOrder[new_order] = window;
12655
window->FocusOrder = (short)new_order;
12656
}
12657
12658
// Note technically focus related but rather adjacent and close to BringWindowToFocusFront()
12659
void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
12660
{
12661
ImGuiContext& g = *GImGui;
12662
ImGuiWindow* current_front_window = g.Windows.back();
12663
if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
12664
return;
12665
for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
12666
if (g.Windows[i] == window)
12667
{
12668
memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
12669
g.Windows[g.Windows.Size - 1] = window;
12670
break;
12671
}
12672
}
12673
12674
void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
12675
{
12676
ImGuiContext& g = *GImGui;
12677
if (g.Windows[0] == window)
12678
return;
12679
for (int i = 0; i < g.Windows.Size; i++)
12680
if (g.Windows[i] == window)
12681
{
12682
memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
12683
g.Windows[0] = window;
12684
break;
12685
}
12686
}
12687
12688
void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
12689
{
12690
IM_ASSERT(window != NULL && behind_window != NULL);
12691
ImGuiContext& g = *GImGui;
12692
window = window->RootWindow;
12693
behind_window = behind_window->RootWindow;
12694
int pos_wnd = FindWindowDisplayIndex(window);
12695
int pos_beh = FindWindowDisplayIndex(behind_window);
12696
if (pos_wnd < pos_beh)
12697
{
12698
size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
12699
memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
12700
g.Windows[pos_beh - 1] = window;
12701
}
12702
else
12703
{
12704
size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
12705
memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
12706
g.Windows[pos_beh] = window;
12707
}
12708
}
12709
12710
int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
12711
{
12712
ImGuiContext& g = *GImGui;
12713
return g.Windows.index_from_ptr(g.Windows.find(window));
12714
}
12715
12716
// Moving window to front of display and set focus (which happens to be back of our sorted list)
12717
void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
12718
{
12719
ImGuiContext& g = *GImGui;
12720
12721
// Modal check?
12722
if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
12723
if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
12724
{
12725
// This block would typically be reached in two situations:
12726
// - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
12727
// - User clicking on void or anything behind a modal while a modal is open (window == NULL)
12728
IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
12729
if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
12730
BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
12731
ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
12732
return;
12733
}
12734
12735
// Find last focused child (if any) and focus it instead.
12736
if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
12737
window = NavRestoreLastChildNavWindow(window);
12738
12739
// Apply focus
12740
if (g.NavWindow != window)
12741
{
12742
SetNavWindow(window);
12743
if (window && g.NavHighlightItemUnderNav)
12744
g.NavMousePosDirty = true;
12745
g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
12746
g.NavLayer = ImGuiNavLayer_Main;
12747
SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
12748
g.NavIdIsAlive = false;
12749
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12750
12751
// Close popups if any
12752
ClosePopupsOverWindow(window, false);
12753
}
12754
12755
// Move the root window to the top of the pile
12756
IM_ASSERT(window == NULL || window->RootWindow != NULL);
12757
ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
12758
ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
12759
12760
// Steal active widgets. Some of the cases it triggers includes:
12761
// - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
12762
// - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
12763
if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
12764
if (!g.ActiveIdNoClearOnFocusLoss)
12765
ClearActiveID();
12766
12767
// Passing NULL allow to disable keyboard focus
12768
if (!window)
12769
return;
12770
12771
// Bring to front
12772
BringWindowToFocusFront(focus_front_window);
12773
if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
12774
BringWindowToDisplayFront(display_front_window);
12775
}
12776
12777
void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
12778
{
12779
ImGuiContext& g = *GImGui;
12780
IM_UNUSED(filter_viewport); // Unused in master branch.
12781
int start_idx = g.WindowsFocusOrder.Size - 1;
12782
if (under_this_window != NULL)
12783
{
12784
// Aim at root window behind us, if we are in a child window that's our own root (see #4640)
12785
int offset = -1;
12786
while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
12787
{
12788
under_this_window = under_this_window->ParentWindow;
12789
offset = 0;
12790
}
12791
start_idx = FindWindowFocusIndex(under_this_window) + offset;
12792
}
12793
for (int i = start_idx; i >= 0; i--)
12794
{
12795
// 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.
12796
ImGuiWindow* window = g.WindowsFocusOrder[i];
12797
if (window == ignore_window || !window->WasActive)
12798
continue;
12799
if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
12800
{
12801
FocusWindow(window, flags);
12802
return;
12803
}
12804
}
12805
FocusWindow(NULL, flags);
12806
}
12807
12808
//-----------------------------------------------------------------------------
12809
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
12810
//-----------------------------------------------------------------------------
12811
12812
// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
12813
// In our terminology those should be interchangeable, yet right now this is super confusing.
12814
// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
12815
12816
void ImGui::SetNavCursorVisible(bool visible)
12817
{
12818
ImGuiContext& g = *GImGui;
12819
if (g.IO.ConfigNavCursorVisibleAlways)
12820
visible = true;
12821
g.NavCursorVisible = visible;
12822
}
12823
12824
// (was called NavRestoreHighlightAfterMove() before 1.91.4)
12825
void ImGui::SetNavCursorVisibleAfterMove()
12826
{
12827
ImGuiContext& g = *GImGui;
12828
if (g.IO.ConfigNavCursorVisibleAuto)
12829
g.NavCursorVisible = true;
12830
g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
12831
}
12832
12833
void ImGui::SetNavWindow(ImGuiWindow* window)
12834
{
12835
ImGuiContext& g = *GImGui;
12836
if (g.NavWindow != window)
12837
{
12838
IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
12839
g.NavWindow = window;
12840
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12841
}
12842
g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12843
NavUpdateAnyRequestFlag();
12844
}
12845
12846
void ImGui::NavHighlightActivated(ImGuiID id)
12847
{
12848
ImGuiContext& g = *GImGui;
12849
g.NavHighlightActivatedId = id;
12850
g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
12851
}
12852
12853
void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
12854
{
12855
ImGuiContext& g = *GImGui;
12856
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
12857
}
12858
12859
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
12860
{
12861
ImGuiContext& g = *GImGui;
12862
IM_ASSERT(g.NavWindow != NULL);
12863
IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
12864
g.NavId = id;
12865
g.NavLayer = nav_layer;
12866
SetNavFocusScope(focus_scope_id);
12867
g.NavWindow->NavLastIds[nav_layer] = id;
12868
g.NavWindow->NavRectRel[nav_layer] = rect_rel;
12869
12870
// Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12871
NavClearPreferredPosForAxis(ImGuiAxis_X);
12872
NavClearPreferredPosForAxis(ImGuiAxis_Y);
12873
}
12874
12875
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
12876
{
12877
ImGuiContext& g = *GImGui;
12878
IM_ASSERT(id != 0);
12879
12880
if (g.NavWindow != window)
12881
SetNavWindow(window);
12882
12883
// Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
12884
// Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
12885
const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
12886
g.NavId = id;
12887
g.NavLayer = nav_layer;
12888
SetNavFocusScope(g.CurrentFocusScopeId);
12889
window->NavLastIds[nav_layer] = id;
12890
if (g.LastItemData.ID == id)
12891
window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
12892
12893
if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
12894
g.NavHighlightItemUnderNav = true;
12895
else if (g.IO.ConfigNavCursorVisibleAuto)
12896
g.NavCursorVisible = false;
12897
12898
// Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12899
NavClearPreferredPosForAxis(ImGuiAxis_X);
12900
NavClearPreferredPosForAxis(ImGuiAxis_Y);
12901
}
12902
12903
static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
12904
{
12905
if (ImFabs(dx) > ImFabs(dy))
12906
return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
12907
return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
12908
}
12909
12910
static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
12911
{
12912
if (cand_max < curr_min)
12913
return cand_max - curr_min;
12914
if (curr_max < cand_min)
12915
return cand_min - curr_max;
12916
return 0.0f;
12917
}
12918
12919
// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
12920
static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
12921
{
12922
ImGuiContext& g = *GImGui;
12923
ImGuiWindow* window = g.CurrentWindow;
12924
if (g.NavLayer != window->DC.NavLayerCurrent)
12925
return false;
12926
12927
// FIXME: Those are not good variables names
12928
ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle
12929
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)
12930
g.NavScoringDebugCount++;
12931
12932
// When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
12933
if (window->ParentWindow == g.NavWindow)
12934
{
12935
IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
12936
if (!window->ClipRect.Overlaps(cand))
12937
return false;
12938
cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
12939
}
12940
12941
// Compute distance between boxes
12942
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
12943
float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
12944
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
12945
if (dby != 0.0f && dbx != 0.0f)
12946
dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
12947
float dist_box = ImFabs(dbx) + ImFabs(dby);
12948
12949
// 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)
12950
float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
12951
float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
12952
float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
12953
12954
// Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
12955
ImGuiDir quadrant;
12956
float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
12957
if (dbx != 0.0f || dby != 0.0f)
12958
{
12959
// For non-overlapping boxes, use distance between boxes
12960
// 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
12961
// One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
12962
// Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
12963
dax = dbx;
12964
day = dby;
12965
dist_axial = dist_box;
12966
quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
12967
}
12968
else if (dcx != 0.0f || dcy != 0.0f)
12969
{
12970
// For overlapping boxes with different centers, use distance between centers
12971
dax = dcx;
12972
day = dcy;
12973
dist_axial = dist_center;
12974
quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
12975
}
12976
else
12977
{
12978
// 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)
12979
quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
12980
}
12981
12982
const ImGuiDir move_dir = g.NavMoveDir;
12983
#if IMGUI_DEBUG_NAV_SCORING
12984
char buf[200];
12985
if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
12986
{
12987
if (quadrant == move_dir)
12988
{
12989
ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
12990
ImDrawList* draw_list = GetForegroundDrawList(window);
12991
draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
12992
draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
12993
draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
12994
}
12995
}
12996
const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
12997
const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
12998
if (debug_hovering || debug_tty)
12999
{
13000
ImFormatString(buf, IM_ARRAYSIZE(buf),
13001
"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",
13002
dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
13003
if (debug_hovering)
13004
{
13005
ImDrawList* draw_list = GetForegroundDrawList(window);
13006
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
13007
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
13008
draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
13009
draw_list->AddText(cand.Max, ~0U, buf);
13010
}
13011
if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
13012
}
13013
#endif
13014
13015
// Is it in the quadrant we're interested in moving to?
13016
bool new_best = false;
13017
if (quadrant == move_dir)
13018
{
13019
// Does it beat the current best candidate?
13020
if (dist_box < result->DistBox)
13021
{
13022
result->DistBox = dist_box;
13023
result->DistCenter = dist_center;
13024
return true;
13025
}
13026
if (dist_box == result->DistBox)
13027
{
13028
// Try using distance between center points to break ties
13029
if (dist_center < result->DistCenter)
13030
{
13031
result->DistCenter = dist_center;
13032
new_best = true;
13033
}
13034
else if (dist_center == result->DistCenter)
13035
{
13036
// Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
13037
// (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),
13038
// 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.
13039
if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
13040
new_best = true;
13041
}
13042
}
13043
}
13044
13045
// 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
13046
// are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
13047
// 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.
13048
// 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.
13049
// Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
13050
if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
13051
if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
13052
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))
13053
{
13054
result->DistAxial = dist_axial;
13055
new_best = true;
13056
}
13057
13058
return new_best;
13059
}
13060
13061
static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
13062
{
13063
ImGuiContext& g = *GImGui;
13064
ImGuiWindow* window = g.CurrentWindow;
13065
result->Window = window;
13066
result->ID = g.LastItemData.ID;
13067
result->FocusScopeId = g.CurrentFocusScopeId;
13068
result->ItemFlags = g.LastItemData.ItemFlags;
13069
result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
13070
if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13071
{
13072
IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13073
result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13074
}
13075
}
13076
13077
// True when current work location may be scrolled horizontally when moving left / right.
13078
// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
13079
void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
13080
{
13081
ImGuiContext& g = *GImGui;
13082
ImGuiWindow* window = g.CurrentWindow;
13083
window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
13084
}
13085
13086
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
13087
// This is called after LastItemData is set, but NextItemData is also still valid.
13088
static void ImGui::NavProcessItem()
13089
{
13090
ImGuiContext& g = *GImGui;
13091
ImGuiWindow* window = g.CurrentWindow;
13092
const ImGuiID id = g.LastItemData.ID;
13093
const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
13094
13095
// When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)
13096
if (window->DC.NavIsScrollPushableX == false)
13097
{
13098
g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13099
g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13100
}
13101
const ImRect nav_bb = g.LastItemData.NavRect;
13102
13103
// Process Init Request
13104
if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
13105
{
13106
// Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
13107
const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
13108
if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
13109
{
13110
NavApplyItemToResult(&g.NavInitResult);
13111
}
13112
if (candidate_for_nav_default_focus)
13113
{
13114
g.NavInitRequest = false; // Found a match, clear request
13115
NavUpdateAnyRequestFlag();
13116
}
13117
}
13118
13119
// Process Move Request (scoring for navigation)
13120
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
13121
if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
13122
{
13123
if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
13124
{
13125
const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13126
if (is_tabbing)
13127
{
13128
NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
13129
}
13130
else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
13131
{
13132
ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
13133
if (NavScoreItem(result))
13134
NavApplyItemToResult(result);
13135
13136
// Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
13137
const float VISIBLE_RATIO = 0.70f;
13138
if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
13139
if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
13140
if (NavScoreItem(&g.NavMoveResultLocalVisible))
13141
NavApplyItemToResult(&g.NavMoveResultLocalVisible);
13142
}
13143
}
13144
}
13145
13146
// Update information for currently focused/navigated item
13147
if (g.NavId == id)
13148
{
13149
if (g.NavWindow != window)
13150
SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
13151
g.NavLayer = window->DC.NavLayerCurrent;
13152
SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
13153
g.NavFocusScopeId = g.CurrentFocusScopeId;
13154
g.NavIdIsAlive = true;
13155
if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13156
{
13157
IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13158
g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13159
}
13160
window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
13161
}
13162
}
13163
13164
// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
13165
// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
13166
// - Case 1: no nav/active id: set result to first eligible item, stop storing.
13167
// - Case 2: tab forward: on ref id set counter, on counter elapse store result
13168
// - 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
13169
// - Case 4: tab backward: store all results, on ref id pick prev, stop storing
13170
// - 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
13171
void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
13172
{
13173
ImGuiContext& g = *GImGui;
13174
13175
if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
13176
{
13177
if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
13178
return;
13179
if (g.NavFocusScopeId != g.CurrentFocusScopeId)
13180
return;
13181
}
13182
13183
// - Can always land on an item when using API call.
13184
// - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
13185
// - Tabbing without _NavEnableKeyboard: goes through inputable items only.
13186
bool can_stop;
13187
if (move_flags & ImGuiNavMoveFlags_FocusApi)
13188
can_stop = true;
13189
else
13190
can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
13191
13192
// Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
13193
ImGuiNavItemData* result = &g.NavMoveResultLocal;
13194
if (g.NavTabbingDir == +1)
13195
{
13196
// Tab Forward or SetKeyboardFocusHere() with >= 0
13197
if (can_stop && g.NavTabbingResultFirst.ID == 0)
13198
NavApplyItemToResult(&g.NavTabbingResultFirst);
13199
if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
13200
NavMoveRequestResolveWithLastItem(result);
13201
else if (g.NavId == id)
13202
g.NavTabbingCounter = 1;
13203
}
13204
else if (g.NavTabbingDir == -1)
13205
{
13206
// Tab Backward
13207
if (g.NavId == id)
13208
{
13209
if (result->ID)
13210
{
13211
g.NavMoveScoringItems = false;
13212
NavUpdateAnyRequestFlag();
13213
}
13214
}
13215
else if (can_stop)
13216
{
13217
// Keep applying until reaching NavId
13218
NavApplyItemToResult(result);
13219
}
13220
}
13221
else if (g.NavTabbingDir == 0)
13222
{
13223
if (can_stop && g.NavId == id)
13224
NavMoveRequestResolveWithLastItem(result);
13225
if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
13226
NavApplyItemToResult(&g.NavTabbingResultFirst);
13227
}
13228
}
13229
13230
bool ImGui::NavMoveRequestButNoResultYet()
13231
{
13232
ImGuiContext& g = *GImGui;
13233
return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
13234
}
13235
13236
// FIXME: ScoringRect is not set
13237
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13238
{
13239
ImGuiContext& g = *GImGui;
13240
IM_ASSERT(g.NavWindow != NULL);
13241
//IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
13242
13243
if (move_flags & ImGuiNavMoveFlags_IsTabbing)
13244
move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
13245
13246
g.NavMoveSubmitted = g.NavMoveScoringItems = true;
13247
g.NavMoveDir = move_dir;
13248
g.NavMoveDirForDebug = move_dir;
13249
g.NavMoveClipDir = clip_dir;
13250
g.NavMoveFlags = move_flags;
13251
g.NavMoveScrollFlags = scroll_flags;
13252
g.NavMoveForwardToNextFrame = false;
13253
g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
13254
g.NavMoveResultLocal.Clear();
13255
g.NavMoveResultLocalVisible.Clear();
13256
g.NavMoveResultOther.Clear();
13257
g.NavTabbingCounter = 0;
13258
g.NavTabbingResultFirst.Clear();
13259
NavUpdateAnyRequestFlag();
13260
}
13261
13262
void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
13263
{
13264
ImGuiContext& g = *GImGui;
13265
g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
13266
NavApplyItemToResult(result);
13267
NavUpdateAnyRequestFlag();
13268
}
13269
13270
// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsToParent
13271
void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data)
13272
{
13273
ImGuiContext& g = *GImGui;
13274
g.NavMoveScoringItems = false;
13275
g.LastItemData.ID = tree_node_data->ID;
13276
g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
13277
g.LastItemData.NavRect = tree_node_data->NavRect;
13278
NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
13279
NavClearPreferredPosForAxis(ImGuiAxis_Y);
13280
NavUpdateAnyRequestFlag();
13281
}
13282
13283
void ImGui::NavMoveRequestCancel()
13284
{
13285
ImGuiContext& g = *GImGui;
13286
g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13287
NavUpdateAnyRequestFlag();
13288
}
13289
13290
// Forward will reuse the move request again on the next frame (generally with modifications done to it)
13291
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13292
{
13293
ImGuiContext& g = *GImGui;
13294
IM_ASSERT(g.NavMoveForwardToNextFrame == false);
13295
NavMoveRequestCancel();
13296
g.NavMoveForwardToNextFrame = true;
13297
g.NavMoveDir = move_dir;
13298
g.NavMoveClipDir = clip_dir;
13299
g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
13300
g.NavMoveScrollFlags = scroll_flags;
13301
}
13302
13303
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
13304
// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
13305
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
13306
{
13307
ImGuiContext& g = *GImGui;
13308
IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
13309
13310
// In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
13311
// as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
13312
if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
13313
g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
13314
}
13315
13316
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
13317
// This way we could find the last focused window among our children. It would be much less confusing this way?
13318
static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
13319
{
13320
ImGuiWindow* parent = nav_window;
13321
while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
13322
parent = parent->ParentWindow;
13323
if (parent && parent != nav_window)
13324
parent->NavLastChildNavWindow = nav_window;
13325
}
13326
13327
// Restore the last focused child.
13328
// Call when we are expected to land on the Main Layer (0) after FocusWindow()
13329
static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
13330
{
13331
if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
13332
return window->NavLastChildNavWindow;
13333
return window;
13334
}
13335
13336
void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
13337
{
13338
ImGuiContext& g = *GImGui;
13339
if (layer == ImGuiNavLayer_Main)
13340
{
13341
ImGuiWindow* prev_nav_window = g.NavWindow;
13342
g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests?
13343
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13344
if (prev_nav_window)
13345
IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
13346
}
13347
ImGuiWindow* window = g.NavWindow;
13348
if (window->NavLastIds[layer] != 0)
13349
{
13350
SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
13351
}
13352
else
13353
{
13354
g.NavLayer = layer;
13355
NavInitWindow(window, true);
13356
}
13357
}
13358
13359
static inline void ImGui::NavUpdateAnyRequestFlag()
13360
{
13361
ImGuiContext& g = *GImGui;
13362
g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
13363
if (g.NavAnyRequest)
13364
IM_ASSERT(g.NavWindow != NULL);
13365
}
13366
13367
// This needs to be called before we submit any widget (aka in or before Begin)
13368
void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
13369
{
13370
ImGuiContext& g = *GImGui;
13371
IM_ASSERT(window == g.NavWindow);
13372
13373
if (window->Flags & ImGuiWindowFlags_NoNavInputs)
13374
{
13375
g.NavId = 0;
13376
SetNavFocusScope(window->NavRootFocusScopeId);
13377
return;
13378
}
13379
13380
bool init_for_nav = false;
13381
if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
13382
init_for_nav = true;
13383
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
13384
if (init_for_nav)
13385
{
13386
SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
13387
g.NavInitRequest = true;
13388
g.NavInitRequestFromMove = false;
13389
g.NavInitResult.ID = 0;
13390
NavUpdateAnyRequestFlag();
13391
}
13392
else
13393
{
13394
g.NavId = window->NavLastIds[0];
13395
SetNavFocusScope(window->NavRootFocusScopeId);
13396
}
13397
}
13398
13399
static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
13400
{
13401
ImGuiContext& g = *GImGui;
13402
ImGuiWindow* window = g.NavWindow;
13403
const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13404
13405
// 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.
13406
if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
13407
return ImGuiInputSource_Mouse;
13408
else
13409
return ImGuiInputSource_Keyboard; // or Nav in general
13410
}
13411
13412
static ImVec2 ImGui::NavCalcPreferredRefPos()
13413
{
13414
ImGuiContext& g = *GImGui;
13415
ImGuiWindow* window = g.NavWindow;
13416
ImGuiInputSource source = NavCalcPreferredRefPosSource();
13417
13418
const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13419
13420
// 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.
13421
if (source == ImGuiInputSource_Mouse)
13422
{
13423
// Mouse (we need a fallback in case the mouse becomes invalid after being used)
13424
// 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.
13425
// In theory we could move that +1.0f offset in OpenPopupEx()
13426
ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
13427
return ImVec2(p.x + 1.0f, p.y);
13428
}
13429
else
13430
{
13431
// When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
13432
ImRect ref_rect;
13433
if (activated_shortcut)
13434
ref_rect = g.LastItemData.NavRect;
13435
else
13436
ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
13437
13438
// Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
13439
if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
13440
{
13441
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
13442
ref_rect.Translate(window->Scroll - next_scroll);
13443
}
13444
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()));
13445
ImGuiViewport* viewport = GetMainViewport();
13446
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.
13447
}
13448
}
13449
13450
float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
13451
{
13452
ImGuiContext& g = *GImGui;
13453
float repeat_delay, repeat_rate;
13454
GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
13455
13456
ImGuiKey key_less, key_more;
13457
if (g.NavInputSource == ImGuiInputSource_Gamepad)
13458
{
13459
key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
13460
key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
13461
}
13462
else
13463
{
13464
key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
13465
key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
13466
}
13467
float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
13468
if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
13469
amount = 0.0f;
13470
return amount;
13471
}
13472
13473
static void ImGui::NavUpdate()
13474
{
13475
ImGuiContext& g = *GImGui;
13476
ImGuiIO& io = g.IO;
13477
13478
io.WantSetMousePos = false;
13479
//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);
13480
13481
// Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
13482
// FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
13483
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13484
const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
13485
if (nav_gamepad_active)
13486
for (ImGuiKey key : nav_gamepad_keys_to_change_source)
13487
if (IsKeyDown(key))
13488
g.NavInputSource = ImGuiInputSource_Gamepad;
13489
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13490
const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
13491
if (nav_keyboard_active)
13492
for (ImGuiKey key : nav_keyboard_keys_to_change_source)
13493
if (IsKeyDown(key))
13494
g.NavInputSource = ImGuiInputSource_Keyboard;
13495
13496
// Process navigation init request (select first/default focus)
13497
g.NavJustMovedToId = 0;
13498
g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
13499
if (g.NavInitResult.ID != 0)
13500
NavInitRequestApplyResult();
13501
g.NavInitRequest = false;
13502
g.NavInitRequestFromMove = false;
13503
g.NavInitResult.ID = 0;
13504
13505
// Process navigation move request
13506
if (g.NavMoveSubmitted)
13507
NavMoveRequestApplyResult();
13508
g.NavTabbingCounter = 0;
13509
g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13510
if (g.NavCursorHideFrames > 0)
13511
if (--g.NavCursorHideFrames == 0)
13512
g.NavCursorVisible = true;
13513
13514
// Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
13515
bool set_mouse_pos = false;
13516
if (g.NavMousePosDirty && g.NavIdIsAlive)
13517
if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
13518
set_mouse_pos = true;
13519
g.NavMousePosDirty = false;
13520
IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
13521
13522
// 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
13523
if (g.NavWindow)
13524
NavSaveLastChildNavWindowIntoParent(g.NavWindow);
13525
if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
13526
g.NavWindow->NavLastChildNavWindow = NULL;
13527
13528
// Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
13529
NavUpdateWindowing();
13530
13531
// Set output flags for user application
13532
io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
13533
io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
13534
13535
// Process NavCancel input (to close a popup, get back to parent, clear focus)
13536
NavUpdateCancelRequest();
13537
13538
// Process manual activation request
13539
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
13540
g.NavActivateFlags = ImGuiActivateFlags_None;
13541
if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13542
{
13543
const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
13544
const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
13545
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)) */;
13546
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)) */);
13547
if (g.ActiveId == 0 && activate_pressed)
13548
{
13549
g.NavActivateId = g.NavId;
13550
g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
13551
}
13552
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
13553
{
13554
g.NavActivateId = g.NavId;
13555
g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
13556
}
13557
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
13558
g.NavActivateDownId = g.NavId;
13559
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
13560
{
13561
g.NavActivatePressedId = g.NavId;
13562
NavHighlightActivated(g.NavId);
13563
}
13564
}
13565
if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13566
g.NavCursorVisible = false;
13567
else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
13568
g.NavCursorVisible = true;
13569
if (g.NavActivateId != 0)
13570
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
13571
13572
// Highlight
13573
if (g.NavHighlightActivatedTimer > 0.0f)
13574
g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
13575
if (g.NavHighlightActivatedTimer == 0.0f)
13576
g.NavHighlightActivatedId = 0;
13577
13578
// Process programmatic activation request
13579
// FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
13580
if (g.NavNextActivateId != 0)
13581
{
13582
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
13583
g.NavActivateFlags = g.NavNextActivateFlags;
13584
}
13585
g.NavNextActivateId = 0;
13586
13587
// Process move requests
13588
NavUpdateCreateMoveRequest();
13589
if (g.NavMoveDir == ImGuiDir_None)
13590
NavUpdateCreateTabbingRequest();
13591
NavUpdateAnyRequestFlag();
13592
g.NavIdIsAlive = false;
13593
13594
// Scrolling
13595
if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
13596
{
13597
// *Fallback* manual-scroll with Nav directional keys when window has no navigable item
13598
ImGuiWindow* window = g.NavWindow;
13599
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.
13600
const ImGuiDir move_dir = g.NavMoveDir;
13601
if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
13602
{
13603
if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
13604
SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
13605
if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
13606
SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
13607
}
13608
13609
// *Normal* Manual scroll with LStick
13610
// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
13611
if (nav_gamepad_active)
13612
{
13613
const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
13614
const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
13615
if (scroll_dir.x != 0.0f && window->ScrollbarX)
13616
SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
13617
if (scroll_dir.y != 0.0f)
13618
SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
13619
}
13620
}
13621
13622
// Always prioritize mouse highlight if navigation is disabled
13623
if (!nav_keyboard_active && !nav_gamepad_active)
13624
{
13625
g.NavCursorVisible = false;
13626
g.NavHighlightItemUnderNav = set_mouse_pos = false;
13627
}
13628
13629
// Update mouse position if requested
13630
// (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)
13631
if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
13632
TeleportMousePos(NavCalcPreferredRefPos());
13633
13634
// [DEBUG]
13635
g.NavScoringDebugCount = 0;
13636
#if IMGUI_DEBUG_NAV_RECTS
13637
if (ImGuiWindow* debug_window = g.NavWindow)
13638
{
13639
ImDrawList* draw_list = GetForegroundDrawList(debug_window);
13640
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)); }
13641
//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); }
13642
}
13643
#endif
13644
}
13645
13646
void ImGui::NavInitRequestApplyResult()
13647
{
13648
// 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)
13649
ImGuiContext& g = *GImGui;
13650
if (!g.NavWindow)
13651
return;
13652
13653
ImGuiNavItemData* result = &g.NavInitResult;
13654
if (g.NavId != result->ID)
13655
{
13656
g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13657
g.NavJustMovedToId = result->ID;
13658
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13659
g.NavJustMovedToKeyMods = 0;
13660
g.NavJustMovedToIsTabbing = false;
13661
g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13662
}
13663
13664
// Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
13665
// FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
13666
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13667
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13668
g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
13669
if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13670
g.NavLastValidSelectionUserData = result->SelectionUserData;
13671
if (g.NavInitRequestFromMove)
13672
SetNavCursorVisibleAfterMove();
13673
}
13674
13675
// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
13676
static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
13677
{
13678
// Bias initial rect
13679
ImGuiContext& g = *GImGui;
13680
const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
13681
13682
// Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
13683
// - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
13684
// - But each successful move sets new bias on one axis, only cleared when using mouse.
13685
if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
13686
{
13687
if (preferred_pos_rel.x == FLT_MAX)
13688
preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
13689
if (preferred_pos_rel.y == FLT_MAX)
13690
preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
13691
}
13692
13693
// Apply general bias on the other axis
13694
if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
13695
r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
13696
else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
13697
r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
13698
}
13699
13700
void ImGui::NavUpdateCreateMoveRequest()
13701
{
13702
ImGuiContext& g = *GImGui;
13703
ImGuiIO& io = g.IO;
13704
ImGuiWindow* window = g.NavWindow;
13705
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13706
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13707
13708
if (g.NavMoveForwardToNextFrame && window != NULL)
13709
{
13710
// 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)
13711
// (preserve most state, which were already set by the NavMoveRequestForward() function)
13712
IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
13713
IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
13714
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
13715
}
13716
else
13717
{
13718
// Initiate directional inputs request
13719
g.NavMoveDir = ImGuiDir_None;
13720
g.NavMoveFlags = ImGuiNavMoveFlags_None;
13721
g.NavMoveScrollFlags = ImGuiScrollFlags_None;
13722
if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
13723
{
13724
const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
13725
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; }
13726
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; }
13727
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; }
13728
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; }
13729
}
13730
g.NavMoveClipDir = g.NavMoveDir;
13731
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
13732
}
13733
13734
// Update PageUp/PageDown/Home/End scroll
13735
// FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
13736
float scoring_rect_offset_y = 0.0f;
13737
if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
13738
scoring_rect_offset_y = NavUpdatePageUpPageDown();
13739
if (scoring_rect_offset_y != 0.0f)
13740
{
13741
g.NavScoringNoClipRect = window->InnerRect;
13742
g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
13743
}
13744
13745
// [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
13746
#if IMGUI_DEBUG_NAV_SCORING
13747
//if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
13748
// g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
13749
if (io.KeyCtrl)
13750
{
13751
if (g.NavMoveDir == ImGuiDir_None)
13752
g.NavMoveDir = g.NavMoveDirForDebug;
13753
g.NavMoveClipDir = g.NavMoveDir;
13754
g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
13755
}
13756
#endif
13757
13758
// Submit
13759
g.NavMoveForwardToNextFrame = false;
13760
if (g.NavMoveDir != ImGuiDir_None)
13761
NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
13762
13763
// Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
13764
if (g.NavMoveSubmitted && g.NavId == 0)
13765
{
13766
IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
13767
g.NavInitRequest = g.NavInitRequestFromMove = true;
13768
g.NavInitResult.ID = 0;
13769
if (g.IO.ConfigNavCursorVisibleAuto)
13770
g.NavCursorVisible = true;
13771
}
13772
13773
// When using gamepad, we project the reference nav bounding box into window visible area.
13774
// This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
13775
// since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
13776
if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
13777
{
13778
bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
13779
bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
13780
ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
13781
13782
// Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
13783
// Otherwise 'inner_rect_rel' would be off on the move result frame.
13784
inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
13785
13786
if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
13787
{
13788
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
13789
float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f);
13790
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
13791
inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
13792
inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
13793
inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
13794
inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
13795
window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
13796
g.NavId = 0;
13797
}
13798
}
13799
13800
// 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)
13801
ImRect scoring_rect;
13802
if (window != NULL)
13803
{
13804
ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
13805
scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
13806
scoring_rect.TranslateY(scoring_rect_offset_y);
13807
if (g.NavMoveSubmitted)
13808
NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
13809
IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
13810
//GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
13811
//if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
13812
}
13813
g.NavScoringRect = scoring_rect;
13814
//g.NavScoringNoClipRect.Add(scoring_rect);
13815
}
13816
13817
void ImGui::NavUpdateCreateTabbingRequest()
13818
{
13819
ImGuiContext& g = *GImGui;
13820
ImGuiWindow* window = g.NavWindow;
13821
IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
13822
if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
13823
return;
13824
13825
const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
13826
if (!tab_pressed)
13827
return;
13828
13829
// Initiate tabbing request
13830
// (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
13831
// See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
13832
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13833
if (nav_keyboard_active)
13834
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
13835
else
13836
g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
13837
ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
13838
ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
13839
ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
13840
NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
13841
g.NavTabbingCounter = -1;
13842
}
13843
13844
// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
13845
void ImGui::NavMoveRequestApplyResult()
13846
{
13847
ImGuiContext& g = *GImGui;
13848
#if IMGUI_DEBUG_NAV_SCORING
13849
if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
13850
return;
13851
#endif
13852
13853
// Select which result to use
13854
ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
13855
13856
// Tabbing forward wrap
13857
if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
13858
if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
13859
result = &g.NavTabbingResultFirst;
13860
13861
// 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)
13862
const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
13863
if (result == NULL)
13864
{
13865
if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13866
g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
13867
if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13868
SetNavCursorVisibleAfterMove();
13869
NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
13870
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
13871
return;
13872
}
13873
13874
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
13875
if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
13876
if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
13877
result = &g.NavMoveResultLocalVisible;
13878
13879
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
13880
if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
13881
if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
13882
result = &g.NavMoveResultOther;
13883
IM_ASSERT(g.NavWindow && result->Window);
13884
13885
// Scroll to keep newly navigated item fully into view.
13886
if (g.NavLayer == ImGuiNavLayer_Main)
13887
{
13888
ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
13889
ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
13890
13891
if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
13892
{
13893
// FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
13894
float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
13895
SetScrollY(result->Window, scroll_target);
13896
}
13897
}
13898
13899
if (g.NavWindow != result->Window)
13900
{
13901
IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
13902
g.NavWindow = result->Window;
13903
g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13904
}
13905
13906
// Clear active id unless requested not to
13907
// FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
13908
// so this is mostly provided as a gateway for further experiments (see #1418, #2890)
13909
if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
13910
ClearActiveID();
13911
13912
// Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
13913
// PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
13914
if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
13915
{
13916
g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13917
g.NavJustMovedToId = result->ID;
13918
g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13919
g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
13920
g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13921
g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13922
//IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
13923
}
13924
13925
// Apply new NavID/Focus
13926
IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13927
ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
13928
SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13929
if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13930
g.NavLastValidSelectionUserData = result->SelectionUserData;
13931
13932
// Restore last preferred position for current axis
13933
// (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
13934
if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
13935
{
13936
preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
13937
g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
13938
}
13939
13940
// Tabbing: Activates Inputable, otherwise only Focus
13941
if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
13942
g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
13943
13944
// Activate
13945
if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
13946
{
13947
g.NavNextActivateId = result->ID;
13948
g.NavNextActivateFlags = ImGuiActivateFlags_None;
13949
if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13950
g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
13951
}
13952
13953
// Make nav cursor visible
13954
if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13955
SetNavCursorVisibleAfterMove();
13956
}
13957
13958
// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
13959
// FIXME: In order to support e.g. Escape to clear a selection we'll need:
13960
// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
13961
// - 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
13962
static void ImGui::NavUpdateCancelRequest()
13963
{
13964
ImGuiContext& g = *GImGui;
13965
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13966
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13967
if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
13968
return;
13969
13970
IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
13971
if (g.ActiveId != 0)
13972
{
13973
ClearActiveID();
13974
}
13975
else if (g.NavLayer != ImGuiNavLayer_Main)
13976
{
13977
// Leave the "menu" layer
13978
NavRestoreLayer(ImGuiNavLayer_Main);
13979
SetNavCursorVisibleAfterMove();
13980
}
13981
else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
13982
{
13983
// Exit child window
13984
ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
13985
ImGuiWindow* parent_window = child_window->ParentWindow;
13986
IM_ASSERT(child_window->ChildId != 0);
13987
FocusWindow(parent_window);
13988
SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
13989
SetNavCursorVisibleAfterMove();
13990
}
13991
else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
13992
{
13993
// Close open popup/menu
13994
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
13995
}
13996
#if 0
13997
// DUCKSTATION-CHANGE: We want to keep nav active, since we handle menu exits ourselves.
13998
else
13999
{
14000
// Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
14001
// FIXME-NAV: This should happen on window appearing.
14002
if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14003
if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
14004
g.NavWindow->NavLastIds[0] = 0;
14005
14006
// Clear nav focus
14007
if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14008
g.NavId = 0;
14009
if (g.IO.ConfigNavEscapeClearFocusWindow)
14010
FocusWindow(NULL);
14011
}
14012
#endif
14013
}
14014
14015
// Handle PageUp/PageDown/Home/End keys
14016
// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
14017
// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
14018
// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
14019
static float ImGui::NavUpdatePageUpPageDown()
14020
{
14021
ImGuiContext& g = *GImGui;
14022
ImGuiWindow* window = g.NavWindow;
14023
if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
14024
return 0.0f;
14025
14026
const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
14027
const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
14028
const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14029
const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14030
if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
14031
return 0.0f;
14032
14033
if (g.NavLayer != ImGuiNavLayer_Main)
14034
NavRestoreLayer(ImGuiNavLayer_Main);
14035
14036
if ((window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Main)) == 0 && window->DC.NavWindowHasScrollY)
14037
{
14038
// Fallback manual-scroll when window has no navigable item
14039
if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14040
SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
14041
else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14042
SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
14043
else if (home_pressed)
14044
SetScrollY(window, 0.0f);
14045
else if (end_pressed)
14046
SetScrollY(window, window->ScrollMax.y);
14047
}
14048
else
14049
{
14050
ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
14051
const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight());
14052
float nav_scoring_rect_offset_y = 0.0f;
14053
if (IsKeyPressed(ImGuiKey_PageUp, true))
14054
{
14055
nav_scoring_rect_offset_y = -page_offset_y;
14056
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)
14057
g.NavMoveClipDir = ImGuiDir_Up;
14058
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14059
}
14060
else if (IsKeyPressed(ImGuiKey_PageDown, true))
14061
{
14062
nav_scoring_rect_offset_y = +page_offset_y;
14063
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)
14064
g.NavMoveClipDir = ImGuiDir_Down;
14065
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14066
}
14067
else if (home_pressed)
14068
{
14069
// FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
14070
// Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
14071
// Preserve current horizontal position if we have any.
14072
nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
14073
if (nav_rect_rel.IsInverted())
14074
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14075
g.NavMoveDir = ImGuiDir_Down;
14076
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14077
// FIXME-NAV: MoveClipDir left to _None, intentional?
14078
}
14079
else if (end_pressed)
14080
{
14081
nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
14082
if (nav_rect_rel.IsInverted())
14083
nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14084
g.NavMoveDir = ImGuiDir_Up;
14085
g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14086
// FIXME-NAV: MoveClipDir left to _None, intentional?
14087
}
14088
return nav_scoring_rect_offset_y;
14089
}
14090
return 0.0f;
14091
}
14092
14093
static void ImGui::NavEndFrame()
14094
{
14095
ImGuiContext& g = *GImGui;
14096
14097
// Show CTRL+TAB list window
14098
if (g.NavWindowingTarget != NULL)
14099
NavUpdateWindowingOverlay();
14100
14101
// Perform wrap-around in menus
14102
// 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.
14103
// FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
14104
if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
14105
NavUpdateCreateWrappingRequest();
14106
}
14107
14108
static void ImGui::NavUpdateCreateWrappingRequest()
14109
{
14110
ImGuiContext& g = *GImGui;
14111
ImGuiWindow* window = g.NavWindow;
14112
14113
bool do_forward = false;
14114
ImRect bb_rel = window->NavRectRel[g.NavLayer];
14115
ImGuiDir clip_dir = g.NavMoveDir;
14116
14117
const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
14118
//const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
14119
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14120
{
14121
bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
14122
if (move_flags & ImGuiNavMoveFlags_WrapX)
14123
{
14124
bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
14125
clip_dir = ImGuiDir_Up;
14126
}
14127
do_forward = true;
14128
}
14129
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14130
{
14131
bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
14132
if (move_flags & ImGuiNavMoveFlags_WrapX)
14133
{
14134
bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
14135
clip_dir = ImGuiDir_Down;
14136
}
14137
do_forward = true;
14138
}
14139
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14140
{
14141
bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
14142
if (move_flags & ImGuiNavMoveFlags_WrapY)
14143
{
14144
bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
14145
clip_dir = ImGuiDir_Left;
14146
}
14147
SetScrollY(window, window->ScrollMax.y);
14148
do_forward = true;
14149
}
14150
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14151
{
14152
bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
14153
if (move_flags & ImGuiNavMoveFlags_WrapY)
14154
{
14155
bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
14156
clip_dir = ImGuiDir_Right;
14157
}
14158
SetScrollY(window, 0.0f);
14159
do_forward = true;
14160
}
14161
if (!do_forward)
14162
return;
14163
window->NavRectRel[g.NavLayer] = bb_rel;
14164
NavClearPreferredPosForAxis(ImGuiAxis_X);
14165
NavClearPreferredPosForAxis(ImGuiAxis_Y);
14166
NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
14167
}
14168
14169
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
14170
// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
14171
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
14172
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
14173
{
14174
return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
14175
}
14176
14177
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
14178
{
14179
ImGuiContext& g = *GImGui;
14180
for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
14181
if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
14182
return g.WindowsFocusOrder[i];
14183
return NULL;
14184
}
14185
14186
static void NavUpdateWindowingTarget(int focus_change_dir)
14187
{
14188
ImGuiContext& g = *GImGui;
14189
IM_ASSERT(g.NavWindowingTarget);
14190
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
14191
return;
14192
14193
const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
14194
ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
14195
if (!window_target)
14196
window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
14197
if (window_target) // Don't reset windowing target if there's a single window in the list
14198
{
14199
g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
14200
g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14201
}
14202
g.NavWindowingToggleLayer = false;
14203
}
14204
14205
// Apply focus and close overlay
14206
static void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window)
14207
{
14208
ImGuiContext& g = *GImGui;
14209
if (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)
14210
{
14211
ClearActiveID();
14212
SetNavCursorVisibleAfterMove();
14213
ClosePopupsOverWindow(apply_focus_window, false);
14214
FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
14215
apply_focus_window = g.NavWindow;
14216
if (apply_focus_window->NavLastIds[0] == 0)
14217
NavInitWindow(apply_focus_window, false);
14218
14219
// If the window has ONLY a menu layer (no main layer), select it directly
14220
// Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
14221
// so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
14222
// the target window as already been previewed once.
14223
// FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
14224
// we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
14225
// won't be valid.
14226
if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
14227
g.NavLayer = ImGuiNavLayer_Menu;
14228
}
14229
g.NavWindowingTarget = NULL;
14230
}
14231
14232
// Windowing management mode
14233
// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
14234
// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
14235
static void ImGui::NavUpdateWindowing()
14236
{
14237
ImGuiContext& g = *GImGui;
14238
ImGuiIO& io = g.IO;
14239
14240
ImGuiWindow* apply_focus_window = NULL;
14241
bool apply_toggle_layer = false;
14242
14243
ImGuiWindow* modal_window = GetTopMostPopupModal();
14244
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.
14245
if (!allow_windowing)
14246
g.NavWindowingTarget = NULL;
14247
14248
// Fade out
14249
if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
14250
{
14251
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
14252
if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
14253
g.NavWindowingTargetAnim = NULL;
14254
}
14255
14256
// Start CTRL+Tab or Square+L/R window selection
14257
// (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
14258
const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing");
14259
const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14260
const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14261
const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14262
const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14263
const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id);
14264
const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
14265
bool just_started_windowing_from_null_focus = false;
14266
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
14267
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
14268
{
14269
if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad)
14270
g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
14271
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
14272
g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14273
g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
14274
g.NavWindowingInputSource = g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
14275
if (g.NavWindow == NULL)
14276
just_started_windowing_from_null_focus = true;
14277
14278
// 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.
14279
if (keyboard_next_window || keyboard_prev_window)
14280
SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
14281
}
14282
14283
// Gamepad update
14284
if ((g.NavWindowingTarget || g.NavWindowingToggleLayer) && g.NavWindowingInputSource == ImGuiInputSource_Gamepad)
14285
{
14286
if (g.NavWindowingTarget != NULL)
14287
{
14288
// 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
14289
// However inputs are accepted immediately, so you press ImGuiKey_NavGamepadMenu + L1/R1 fast.
14290
g.NavWindowingTimer += io.DeltaTime;
14291
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
14292
14293
// Select window to focus
14294
const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
14295
if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
14296
{
14297
NavUpdateWindowingTarget(focus_change_dir);
14298
g.NavWindowingHighlightAlpha = 1.0f;
14299
}
14300
}
14301
14302
// Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
14303
if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
14304
{
14305
g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
14306
if (g.NavWindowingToggleLayer && g.NavWindow)
14307
apply_toggle_layer = true;
14308
else if (!g.NavWindowingToggleLayer)
14309
apply_focus_window = g.NavWindowingTarget;
14310
g.NavWindowingTarget = NULL;
14311
g.NavWindowingToggleLayer = false;
14312
}
14313
}
14314
14315
// Keyboard: Focus
14316
if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)
14317
{
14318
// Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
14319
ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
14320
IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
14321
g.NavWindowingTimer += io.DeltaTime;
14322
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
14323
if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
14324
NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);
14325
else if ((io.KeyMods & shared_mods) != shared_mods)
14326
apply_focus_window = g.NavWindowingTarget;
14327
}
14328
14329
// Keyboard: Press and Release ALT to toggle menu layer
14330
const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
14331
bool windowing_toggle_layer_start = false;
14332
if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
14333
for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
14334
if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
14335
{
14336
windowing_toggle_layer_start = true;
14337
g.NavWindowingToggleLayer = true;
14338
g.NavWindowingToggleKey = windowing_toggle_key;
14339
g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Keyboard;
14340
break;
14341
}
14342
if (g.NavWindowingToggleLayer && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)
14343
{
14344
// We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
14345
// We cancel toggling nav layer when other modifiers are pressed. (See #4439)
14346
// - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
14347
// We cancel toggling nav layer if an owner has claimed the key.
14348
if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
14349
g.NavWindowingToggleLayer = false;
14350
else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)
14351
g.NavWindowingToggleLayer = false;
14352
else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
14353
g.NavWindowingToggleLayer = false;
14354
14355
// Apply layer toggle on Alt release
14356
// 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.
14357
if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
14358
if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
14359
if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
14360
apply_toggle_layer = true;
14361
if (!IsKeyDown(g.NavWindowingToggleKey))
14362
g.NavWindowingToggleLayer = false;
14363
}
14364
14365
// Move window
14366
if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
14367
{
14368
ImVec2 nav_move_dir;
14369
if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
14370
nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
14371
if (g.NavInputSource == ImGuiInputSource_Gamepad)
14372
nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
14373
if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
14374
{
14375
const float NAV_MOVE_SPEED = 800.0f;
14376
const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
14377
g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
14378
g.NavHighlightItemUnderNav = true;
14379
ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
14380
if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
14381
{
14382
ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
14383
SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
14384
g.NavWindowingAccumDeltaPos -= accum_floored;
14385
}
14386
}
14387
}
14388
14389
// Apply final focus
14390
if (apply_focus_window)
14391
NavUpdateWindowingApplyFocus(apply_focus_window);
14392
14393
// Apply menu/layer toggle
14394
if (apply_toggle_layer && g.NavWindow)
14395
{
14396
ClearActiveID();
14397
14398
// Move to parent menu if necessary
14399
ImGuiWindow* new_nav_window = g.NavWindow;
14400
while (new_nav_window->ParentWindow
14401
&& (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
14402
&& (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
14403
&& (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
14404
new_nav_window = new_nav_window->ParentWindow;
14405
if (new_nav_window != g.NavWindow)
14406
{
14407
ImGuiWindow* old_nav_window = g.NavWindow;
14408
FocusWindow(new_nav_window);
14409
new_nav_window->NavLastChildNavWindow = old_nav_window;
14410
}
14411
14412
// Toggle layer
14413
const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
14414
if (new_nav_layer != g.NavLayer)
14415
{
14416
// Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
14417
if (new_nav_layer == ImGuiNavLayer_Menu)
14418
g.NavWindow->NavLastIds[new_nav_layer] = 0;
14419
NavRestoreLayer(new_nav_layer);
14420
SetNavCursorVisibleAfterMove();
14421
}
14422
}
14423
}
14424
14425
// Window has already passed the IsWindowNavFocusable()
14426
static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
14427
{
14428
if (window->Flags & ImGuiWindowFlags_Popup)
14429
return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
14430
if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
14431
return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
14432
return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
14433
}
14434
14435
// Overlay displayed when using CTRL+TAB. Called by EndFrame().
14436
void ImGui::NavUpdateWindowingOverlay()
14437
{
14438
ImGuiContext& g = *GImGui;
14439
IM_ASSERT(g.NavWindowingTarget != NULL);
14440
14441
if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
14442
return;
14443
14444
if (g.NavWindowingListWindow == NULL)
14445
g.NavWindowingListWindow = FindWindowByName("##NavWindowingOverlay");
14446
const ImGuiViewport* viewport = GetMainViewport();
14447
SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
14448
SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
14449
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
14450
Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
14451
if (g.ContextName[0] != 0)
14452
SeparatorText(g.ContextName);
14453
for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
14454
{
14455
ImGuiWindow* window = g.WindowsFocusOrder[n];
14456
IM_ASSERT(window != NULL); // Fix static analyzers
14457
if (!IsWindowNavFocusable(window))
14458
continue;
14459
const char* label = window->Name;
14460
if (label == FindRenderedTextEnd(label))
14461
label = GetFallbackWindowNameForWindowingList(window);
14462
Selectable(label, g.NavWindowingTarget == window);
14463
}
14464
End();
14465
PopStyleVar();
14466
}
14467
14468
//-----------------------------------------------------------------------------
14469
// [SECTION] DRAG AND DROP
14470
//-----------------------------------------------------------------------------
14471
14472
bool ImGui::IsDragDropActive()
14473
{
14474
ImGuiContext& g = *GImGui;
14475
return g.DragDropActive;
14476
}
14477
14478
void ImGui::ClearDragDrop()
14479
{
14480
ImGuiContext& g = *GImGui;
14481
if (g.DragDropActive)
14482
IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
14483
g.DragDropActive = false;
14484
g.DragDropPayload.Clear();
14485
g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
14486
g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
14487
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
14488
g.DragDropAcceptFrameCount = -1;
14489
14490
g.DragDropPayloadBufHeap.clear();
14491
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14492
}
14493
14494
bool ImGui::BeginTooltipHidden()
14495
{
14496
ImGuiContext& g = *GImGui;
14497
bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
14498
SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
14499
return ret;
14500
}
14501
14502
// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
14503
// If the item has an identifier:
14504
// - This assume/require the item to be activated (typically via ButtonBehavior).
14505
// - 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.
14506
// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
14507
// If the item has no identifier:
14508
// - Currently always assume left mouse button.
14509
bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
14510
{
14511
ImGuiContext& g = *GImGui;
14512
ImGuiWindow* window = g.CurrentWindow;
14513
14514
// FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
14515
// in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
14516
ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
14517
14518
bool source_drag_active = false;
14519
ImGuiID source_id = 0;
14520
ImGuiID source_parent_id = 0;
14521
if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
14522
{
14523
source_id = g.LastItemData.ID;
14524
if (source_id != 0)
14525
{
14526
// Common path: items with ID
14527
if (g.ActiveId != source_id)
14528
return false;
14529
if (g.ActiveIdMouseButton != -1)
14530
mouse_button = g.ActiveIdMouseButton;
14531
if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14532
return false;
14533
g.ActiveIdAllowOverlap = false;
14534
}
14535
else
14536
{
14537
// Uncommon path: items without ID
14538
if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14539
return false;
14540
if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
14541
return false;
14542
14543
// If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
14544
// A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
14545
if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
14546
{
14547
IM_ASSERT(0);
14548
return false;
14549
}
14550
14551
// Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
14552
// We build a throwaway ID based on current ID stack + relative AABB of items in window.
14553
// THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
14554
// We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
14555
// Rely on keeping other window->LastItemXXX fields intact.
14556
source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
14557
KeepAliveID(source_id);
14558
bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);
14559
if (is_hovered && g.IO.MouseClicked[mouse_button])
14560
{
14561
SetActiveID(source_id, window);
14562
FocusWindow(window);
14563
}
14564
if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
14565
g.ActiveIdAllowOverlap = is_hovered;
14566
}
14567
if (g.ActiveId != source_id)
14568
return false;
14569
source_parent_id = window->IDStack.back();
14570
source_drag_active = IsMouseDragging(mouse_button);
14571
14572
// Disable navigation and key inputs while dragging + cancel existing request if any
14573
SetActiveIdUsingAllKeyboardKeys();
14574
}
14575
else
14576
{
14577
// When ImGuiDragDropFlags_SourceExtern is set:
14578
window = NULL;
14579
source_id = ImHashStr("#SourceExtern");
14580
source_drag_active = true;
14581
mouse_button = g.IO.MouseDown[0] ? 0 : -1;
14582
KeepAliveID(source_id);
14583
SetActiveID(source_id, NULL);
14584
}
14585
14586
IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14587
if (!source_drag_active)
14588
return false;
14589
14590
// Activate drag and drop
14591
if (!g.DragDropActive)
14592
{
14593
IM_ASSERT(source_id != 0);
14594
ClearDragDrop();
14595
IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
14596
source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
14597
ImGuiPayload& payload = g.DragDropPayload;
14598
payload.SourceId = source_id;
14599
payload.SourceParentId = source_parent_id;
14600
g.DragDropActive = true;
14601
g.DragDropSourceFlags = flags;
14602
g.DragDropMouseButton = mouse_button;
14603
if (payload.SourceId == g.ActiveId)
14604
g.ActiveIdNoClearOnFocusLoss = true;
14605
}
14606
g.DragDropSourceFrameCount = g.FrameCount;
14607
g.DragDropWithinSource = true;
14608
14609
if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14610
{
14611
// Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
14612
// We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
14613
bool ret;
14614
if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
14615
ret = BeginTooltipHidden();
14616
else
14617
ret = BeginTooltip();
14618
IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
14619
IM_UNUSED(ret);
14620
}
14621
14622
if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
14623
g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
14624
14625
return true;
14626
}
14627
14628
void ImGui::EndDragDropSource()
14629
{
14630
ImGuiContext& g = *GImGui;
14631
IM_ASSERT(g.DragDropActive);
14632
IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
14633
14634
if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14635
EndTooltip();
14636
14637
// Discard the drag if have not called SetDragDropPayload()
14638
if (g.DragDropPayload.DataFrameCount == -1)
14639
ClearDragDrop();
14640
g.DragDropWithinSource = false;
14641
}
14642
14643
// Use 'cond' to choose to submit payload on drag start or every frame
14644
bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
14645
{
14646
ImGuiContext& g = *GImGui;
14647
ImGuiPayload& payload = g.DragDropPayload;
14648
if (cond == 0)
14649
cond = ImGuiCond_Always;
14650
14651
IM_ASSERT(type != NULL);
14652
IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
14653
IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
14654
IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
14655
IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
14656
14657
if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
14658
{
14659
// Copy payload
14660
ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
14661
g.DragDropPayloadBufHeap.resize(0);
14662
if (data_size > sizeof(g.DragDropPayloadBufLocal))
14663
{
14664
// Store in heap
14665
g.DragDropPayloadBufHeap.resize((int)data_size);
14666
payload.Data = g.DragDropPayloadBufHeap.Data;
14667
memcpy(payload.Data, data, data_size);
14668
}
14669
else if (data_size > 0)
14670
{
14671
// Store locally
14672
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14673
payload.Data = g.DragDropPayloadBufLocal;
14674
memcpy(payload.Data, data, data_size);
14675
}
14676
else
14677
{
14678
payload.Data = NULL;
14679
}
14680
payload.DataSize = (int)data_size;
14681
}
14682
payload.DataFrameCount = g.FrameCount;
14683
14684
// Return whether the payload has been accepted
14685
return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
14686
}
14687
14688
bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
14689
{
14690
ImGuiContext& g = *GImGui;
14691
if (!g.DragDropActive)
14692
return false;
14693
14694
ImGuiWindow* window = g.CurrentWindow;
14695
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14696
if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
14697
return false;
14698
IM_ASSERT(id != 0);
14699
if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
14700
return false;
14701
if (window->SkipItems)
14702
return false;
14703
14704
IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14705
g.DragDropTargetRect = bb;
14706
g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
14707
g.DragDropTargetId = id;
14708
g.DragDropWithinTarget = true;
14709
return true;
14710
}
14711
14712
// We don't use BeginDragDropTargetCustom() and duplicate its code because:
14713
// 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.
14714
// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
14715
// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
14716
bool ImGui::BeginDragDropTarget()
14717
{
14718
ImGuiContext& g = *GImGui;
14719
if (!g.DragDropActive)
14720
return false;
14721
14722
ImGuiWindow* window = g.CurrentWindow;
14723
if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
14724
return false;
14725
ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14726
if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)
14727
return false;
14728
14729
const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
14730
ImGuiID id = g.LastItemData.ID;
14731
if (id == 0)
14732
{
14733
id = window->GetIDFromRectangle(display_rect);
14734
KeepAliveID(id);
14735
}
14736
if (g.DragDropPayload.SourceId == id)
14737
return false;
14738
14739
IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14740
g.DragDropTargetRect = display_rect;
14741
g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
14742
g.DragDropTargetId = id;
14743
g.DragDropWithinTarget = true;
14744
return true;
14745
}
14746
14747
bool ImGui::IsDragDropPayloadBeingAccepted()
14748
{
14749
ImGuiContext& g = *GImGui;
14750
return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
14751
}
14752
14753
const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
14754
{
14755
ImGuiContext& g = *GImGui;
14756
ImGuiPayload& payload = g.DragDropPayload;
14757
IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
14758
IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
14759
if (type != NULL && !payload.IsDataType(type))
14760
return NULL;
14761
14762
// Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
14763
// NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
14764
const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
14765
ImRect r = g.DragDropTargetRect;
14766
float r_surface = r.GetWidth() * r.GetHeight();
14767
if (r_surface > g.DragDropAcceptIdCurrRectSurface)
14768
return NULL;
14769
14770
g.DragDropAcceptFlags = flags;
14771
g.DragDropAcceptIdCurr = g.DragDropTargetId;
14772
g.DragDropAcceptIdCurrRectSurface = r_surface;
14773
//IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
14774
14775
// Render default drop visuals
14776
payload.Preview = was_accepted_previously;
14777
flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
14778
if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
14779
RenderDragDropTargetRect(r, g.DragDropTargetClipRect);
14780
14781
g.DragDropAcceptFrameCount = g.FrameCount;
14782
if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
14783
payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
14784
else
14785
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()
14786
if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
14787
return NULL;
14788
14789
if (payload.Delivery)
14790
IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
14791
return &payload;
14792
}
14793
14794
// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
14795
void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect)
14796
{
14797
ImGuiContext& g = *GImGui;
14798
ImGuiWindow* window = g.CurrentWindow;
14799
ImRect bb_display = bb;
14800
bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
14801
bb_display.Expand(3.5f);
14802
bool push_clip_rect = !window->ClipRect.Contains(bb_display);
14803
if (push_clip_rect)
14804
window->DrawList->PushClipRectFullScreen();
14805
window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); // FIXME-DPI
14806
if (push_clip_rect)
14807
window->DrawList->PopClipRect();
14808
}
14809
14810
const ImGuiPayload* ImGui::GetDragDropPayload()
14811
{
14812
ImGuiContext& g = *GImGui;
14813
return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
14814
}
14815
14816
void ImGui::EndDragDropTarget()
14817
{
14818
ImGuiContext& g = *GImGui;
14819
IM_ASSERT(g.DragDropActive);
14820
IM_ASSERT(g.DragDropWithinTarget);
14821
g.DragDropWithinTarget = false;
14822
14823
// Clear drag and drop state payload right after delivery
14824
if (g.DragDropPayload.Delivery)
14825
ClearDragDrop();
14826
}
14827
14828
//-----------------------------------------------------------------------------
14829
// [SECTION] LOGGING/CAPTURING
14830
//-----------------------------------------------------------------------------
14831
// All text output from the interface can be captured into tty/file/clipboard.
14832
// By default, tree nodes are automatically opened during logging.
14833
//-----------------------------------------------------------------------------
14834
14835
// Pass text data straight to log (without being displayed)
14836
static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
14837
{
14838
if (g.LogFile)
14839
{
14840
g.LogBuffer.Buf.resize(0);
14841
g.LogBuffer.appendfv(fmt, args);
14842
ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
14843
}
14844
else
14845
{
14846
g.LogBuffer.appendfv(fmt, args);
14847
}
14848
}
14849
14850
void ImGui::LogText(const char* fmt, ...)
14851
{
14852
ImGuiContext& g = *GImGui;
14853
if (!g.LogEnabled)
14854
return;
14855
14856
va_list args;
14857
va_start(args, fmt);
14858
LogTextV(g, fmt, args);
14859
va_end(args);
14860
}
14861
14862
void ImGui::LogTextV(const char* fmt, va_list args)
14863
{
14864
ImGuiContext& g = *GImGui;
14865
if (!g.LogEnabled)
14866
return;
14867
14868
LogTextV(g, fmt, args);
14869
}
14870
14871
// Internal version that takes a position to decide on newline placement and pad items according to their depth.
14872
// We split text into individual lines to add current tree level padding
14873
// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
14874
void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
14875
{
14876
ImGuiContext& g = *GImGui;
14877
ImGuiWindow* window = g.CurrentWindow;
14878
14879
const char* prefix = g.LogNextPrefix;
14880
const char* suffix = g.LogNextSuffix;
14881
g.LogNextPrefix = g.LogNextSuffix = NULL;
14882
14883
if (!text_end)
14884
text_end = FindRenderedTextEnd(text, text_end);
14885
14886
const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
14887
if (ref_pos)
14888
g.LogLinePosY = ref_pos->y;
14889
if (log_new_line)
14890
{
14891
LogText(IM_NEWLINE);
14892
g.LogLineFirstItem = true;
14893
}
14894
14895
if (prefix)
14896
LogRenderedText(ref_pos, prefix, prefix + ImStrlen(prefix)); // Calculate end ourself to ensure "##" are included here.
14897
14898
// Re-adjust padding if we have popped out of our starting depth
14899
if (g.LogDepthRef > window->DC.TreeDepth)
14900
g.LogDepthRef = window->DC.TreeDepth;
14901
const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
14902
14903
const char* text_remaining = text;
14904
for (;;)
14905
{
14906
// Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
14907
// We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
14908
const char* line_start = text_remaining;
14909
const char* line_end = ImStreolRange(line_start, text_end);
14910
const bool is_last_line = (line_end == text_end);
14911
if (line_start != line_end || !is_last_line)
14912
{
14913
const int line_length = (int)(line_end - line_start);
14914
const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
14915
LogText("%*s%.*s", indentation, "", line_length, line_start);
14916
g.LogLineFirstItem = false;
14917
if (*line_end == '\n')
14918
{
14919
LogText(IM_NEWLINE);
14920
g.LogLineFirstItem = true;
14921
}
14922
}
14923
if (is_last_line)
14924
break;
14925
text_remaining = line_end + 1;
14926
}
14927
14928
if (suffix)
14929
LogRenderedText(ref_pos, suffix, suffix + ImStrlen(suffix));
14930
}
14931
14932
// Start logging/capturing text output
14933
void ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth)
14934
{
14935
ImGuiContext& g = *GImGui;
14936
ImGuiWindow* window = g.CurrentWindow;
14937
IM_ASSERT(g.LogEnabled == false);
14938
IM_ASSERT(g.LogFile == NULL && g.LogBuffer.empty());
14939
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiLogFlags_OutputMask_)); // Check that only 1 type flag is used
14940
14941
g.LogEnabled = g.ItemUnclipByLog = true;
14942
g.LogFlags = flags;
14943
g.LogWindow = window;
14944
g.LogNextPrefix = g.LogNextSuffix = NULL;
14945
g.LogDepthRef = window->DC.TreeDepth;
14946
g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
14947
g.LogLinePosY = FLT_MAX;
14948
g.LogLineFirstItem = true;
14949
}
14950
14951
// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
14952
void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
14953
{
14954
ImGuiContext& g = *GImGui;
14955
g.LogNextPrefix = prefix;
14956
g.LogNextSuffix = suffix;
14957
}
14958
14959
void ImGui::LogToTTY(int auto_open_depth)
14960
{
14961
ImGuiContext& g = *GImGui;
14962
if (g.LogEnabled)
14963
return;
14964
IM_UNUSED(auto_open_depth);
14965
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
14966
LogBegin(ImGuiLogFlags_OutputTTY, auto_open_depth);
14967
g.LogFile = stdout;
14968
#endif
14969
}
14970
14971
// Start logging/capturing text output to given file
14972
void ImGui::LogToFile(int auto_open_depth, const char* filename)
14973
{
14974
ImGuiContext& g = *GImGui;
14975
if (g.LogEnabled)
14976
return;
14977
14978
// FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
14979
// be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
14980
// By opening the file in binary mode "ab" we have consistent output everywhere.
14981
if (!filename)
14982
filename = g.IO.LogFilename;
14983
if (!filename || !filename[0])
14984
return;
14985
ImFileHandle f = ImFileOpen(filename, "ab");
14986
if (!f)
14987
{
14988
IM_ASSERT(0);
14989
return;
14990
}
14991
14992
LogBegin(ImGuiLogFlags_OutputFile, auto_open_depth);
14993
g.LogFile = f;
14994
}
14995
14996
// Start logging/capturing text output to clipboard
14997
void ImGui::LogToClipboard(int auto_open_depth)
14998
{
14999
ImGuiContext& g = *GImGui;
15000
if (g.LogEnabled)
15001
return;
15002
LogBegin(ImGuiLogFlags_OutputClipboard, auto_open_depth);
15003
}
15004
15005
void ImGui::LogToBuffer(int auto_open_depth)
15006
{
15007
ImGuiContext& g = *GImGui;
15008
if (g.LogEnabled)
15009
return;
15010
LogBegin(ImGuiLogFlags_OutputBuffer, auto_open_depth);
15011
}
15012
15013
void ImGui::LogFinish()
15014
{
15015
ImGuiContext& g = *GImGui;
15016
if (!g.LogEnabled)
15017
return;
15018
15019
LogText(IM_NEWLINE);
15020
switch (g.LogFlags & ImGuiLogFlags_OutputMask_)
15021
{
15022
case ImGuiLogFlags_OutputTTY:
15023
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15024
fflush(g.LogFile);
15025
#endif
15026
break;
15027
case ImGuiLogFlags_OutputFile:
15028
ImFileClose(g.LogFile);
15029
break;
15030
case ImGuiLogFlags_OutputBuffer:
15031
break;
15032
case ImGuiLogFlags_OutputClipboard:
15033
if (!g.LogBuffer.empty())
15034
SetClipboardText(g.LogBuffer.begin());
15035
break;
15036
default:
15037
IM_ASSERT(0);
15038
break;
15039
}
15040
15041
g.LogEnabled = g.ItemUnclipByLog = false;
15042
g.LogFlags = ImGuiLogFlags_None;
15043
g.LogFile = NULL;
15044
g.LogBuffer.clear();
15045
}
15046
15047
// Helper to display logging buttons
15048
// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
15049
void ImGui::LogButtons()
15050
{
15051
ImGuiContext& g = *GImGui;
15052
15053
PushID("LogButtons");
15054
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15055
const bool log_to_tty = Button("Log To TTY"); SameLine();
15056
#else
15057
const bool log_to_tty = false;
15058
#endif
15059
const bool log_to_file = Button("Log To File"); SameLine();
15060
const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
15061
PushItemFlag(ImGuiItemFlags_NoTabStop, true);
15062
SetNextItemWidth(80.0f);
15063
SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
15064
PopItemFlag();
15065
PopID();
15066
15067
// Start logging at the end of the function so that the buttons don't appear in the log
15068
if (log_to_tty)
15069
LogToTTY();
15070
if (log_to_file)
15071
LogToFile();
15072
if (log_to_clipboard)
15073
LogToClipboard();
15074
}
15075
15076
//-----------------------------------------------------------------------------
15077
// [SECTION] SETTINGS
15078
//-----------------------------------------------------------------------------
15079
// - UpdateSettings() [Internal]
15080
// - MarkIniSettingsDirty() [Internal]
15081
// - FindSettingsHandler() [Internal]
15082
// - ClearIniSettings() [Internal]
15083
// - LoadIniSettingsFromDisk()
15084
// - LoadIniSettingsFromMemory()
15085
// - SaveIniSettingsToDisk()
15086
// - SaveIniSettingsToMemory()
15087
//-----------------------------------------------------------------------------
15088
// - CreateNewWindowSettings() [Internal]
15089
// - FindWindowSettingsByID() [Internal]
15090
// - FindWindowSettingsByWindow() [Internal]
15091
// - ClearWindowSettings() [Internal]
15092
// - WindowSettingsHandler_***() [Internal]
15093
//-----------------------------------------------------------------------------
15094
15095
// Called by NewFrame()
15096
void ImGui::UpdateSettings()
15097
{
15098
// Load settings on first frame (if not explicitly loaded manually before)
15099
ImGuiContext& g = *GImGui;
15100
if (!g.SettingsLoaded)
15101
{
15102
IM_ASSERT(g.SettingsWindows.empty());
15103
if (g.IO.IniFilename)
15104
LoadIniSettingsFromDisk(g.IO.IniFilename);
15105
g.SettingsLoaded = true;
15106
}
15107
15108
// Save settings (with a delay after the last modification, so we don't spam disk too much)
15109
if (g.SettingsDirtyTimer > 0.0f)
15110
{
15111
g.SettingsDirtyTimer -= g.IO.DeltaTime;
15112
if (g.SettingsDirtyTimer <= 0.0f)
15113
{
15114
if (g.IO.IniFilename != NULL)
15115
SaveIniSettingsToDisk(g.IO.IniFilename);
15116
else
15117
g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
15118
g.SettingsDirtyTimer = 0.0f;
15119
}
15120
}
15121
}
15122
15123
void ImGui::MarkIniSettingsDirty()
15124
{
15125
ImGuiContext& g = *GImGui;
15126
if (g.SettingsDirtyTimer <= 0.0f)
15127
g.SettingsDirtyTimer = g.IO.IniSavingRate;
15128
}
15129
15130
void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
15131
{
15132
ImGuiContext& g = *GImGui;
15133
if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
15134
if (g.SettingsDirtyTimer <= 0.0f)
15135
g.SettingsDirtyTimer = g.IO.IniSavingRate;
15136
}
15137
15138
void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
15139
{
15140
ImGuiContext& g = *GImGui;
15141
IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
15142
g.SettingsHandlers.push_back(*handler);
15143
}
15144
15145
void ImGui::RemoveSettingsHandler(const char* type_name)
15146
{
15147
ImGuiContext& g = *GImGui;
15148
if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
15149
g.SettingsHandlers.erase(handler);
15150
}
15151
15152
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
15153
{
15154
ImGuiContext& g = *GImGui;
15155
const ImGuiID type_hash = ImHashStr(type_name);
15156
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15157
if (handler.TypeHash == type_hash)
15158
return &handler;
15159
return NULL;
15160
}
15161
15162
// Clear all settings (windows, tables, docking etc.)
15163
void ImGui::ClearIniSettings()
15164
{
15165
ImGuiContext& g = *GImGui;
15166
g.SettingsIniData.clear();
15167
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15168
if (handler.ClearAllFn != NULL)
15169
handler.ClearAllFn(&g, &handler);
15170
}
15171
15172
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
15173
{
15174
size_t file_data_size = 0;
15175
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
15176
if (!file_data)
15177
return;
15178
if (file_data_size > 0)
15179
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
15180
IM_FREE(file_data);
15181
}
15182
15183
// Zero-tolerance, no error reporting, cheap .ini parsing
15184
// 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!
15185
void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
15186
{
15187
ImGuiContext& g = *GImGui;
15188
IM_ASSERT(g.Initialized);
15189
//IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
15190
//IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
15191
15192
// For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
15193
// 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..
15194
if (ini_size == 0)
15195
ini_size = ImStrlen(ini_data);
15196
g.SettingsIniData.Buf.resize((int)ini_size + 1);
15197
char* const buf = g.SettingsIniData.Buf.Data;
15198
char* const buf_end = buf + ini_size;
15199
memcpy(buf, ini_data, ini_size);
15200
buf_end[0] = 0;
15201
15202
// Call pre-read handlers
15203
// Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
15204
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15205
if (handler.ReadInitFn != NULL)
15206
handler.ReadInitFn(&g, &handler);
15207
15208
void* entry_data = NULL;
15209
ImGuiSettingsHandler* entry_handler = NULL;
15210
15211
char* line_end = NULL;
15212
for (char* line = buf; line < buf_end; line = line_end + 1)
15213
{
15214
// Skip new lines markers, then find end of the line
15215
while (*line == '\n' || *line == '\r')
15216
line++;
15217
line_end = line;
15218
while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
15219
line_end++;
15220
line_end[0] = 0;
15221
if (line[0] == ';')
15222
continue;
15223
if (line[0] == '[' && line_end > line && line_end[-1] == ']')
15224
{
15225
// Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
15226
line_end[-1] = 0;
15227
const char* name_end = line_end - 1;
15228
const char* type_start = line + 1;
15229
char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
15230
const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
15231
if (!type_end || !name_start)
15232
continue;
15233
*type_end = 0; // Overwrite first ']'
15234
name_start++; // Skip second '['
15235
entry_handler = FindSettingsHandler(type_start);
15236
entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
15237
}
15238
else if (entry_handler != NULL && entry_data != NULL)
15239
{
15240
// Let type handler parse the line
15241
entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
15242
}
15243
}
15244
g.SettingsLoaded = true;
15245
15246
// [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
15247
memcpy(buf, ini_data, ini_size);
15248
15249
// Call post-read handlers
15250
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15251
if (handler.ApplyAllFn != NULL)
15252
handler.ApplyAllFn(&g, &handler);
15253
}
15254
15255
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
15256
{
15257
ImGuiContext& g = *GImGui;
15258
g.SettingsDirtyTimer = 0.0f;
15259
if (!ini_filename)
15260
return;
15261
15262
size_t ini_data_size = 0;
15263
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
15264
ImFileHandle f = ImFileOpen(ini_filename, "wt");
15265
if (!f)
15266
return;
15267
ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
15268
ImFileClose(f);
15269
}
15270
15271
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
15272
const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
15273
{
15274
ImGuiContext& g = *GImGui;
15275
g.SettingsDirtyTimer = 0.0f;
15276
g.SettingsIniData.Buf.resize(0);
15277
g.SettingsIniData.Buf.push_back(0);
15278
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15279
handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
15280
if (out_size)
15281
*out_size = (size_t)g.SettingsIniData.size();
15282
return g.SettingsIniData.c_str();
15283
}
15284
15285
ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
15286
{
15287
ImGuiContext& g = *GImGui;
15288
15289
if (g.IO.ConfigDebugIniSettings == false)
15290
{
15291
// Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
15292
// Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
15293
if (const char* p = strstr(name, "###"))
15294
name = p;
15295
}
15296
const size_t name_len = ImStrlen(name);
15297
15298
// Allocate chunk
15299
const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
15300
ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
15301
IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
15302
settings->ID = ImHashStr(name, name_len);
15303
memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
15304
15305
return settings;
15306
}
15307
15308
// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
15309
// This is called once per window .ini entry + once per newly instantiated window.
15310
ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
15311
{
15312
ImGuiContext& g = *GImGui;
15313
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15314
if (settings->ID == id && !settings->WantDelete)
15315
return settings;
15316
return NULL;
15317
}
15318
15319
// This is faster if you are holding on a Window already as we don't need to perform a search.
15320
ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
15321
{
15322
ImGuiContext& g = *GImGui;
15323
if (window->SettingsOffset != -1)
15324
return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
15325
return FindWindowSettingsByID(window->ID);
15326
}
15327
15328
// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
15329
void ImGui::ClearWindowSettings(const char* name)
15330
{
15331
//IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
15332
ImGuiWindow* window = FindWindowByName(name);
15333
if (window != NULL)
15334
{
15335
window->Flags |= ImGuiWindowFlags_NoSavedSettings;
15336
InitOrLoadWindowSettings(window, NULL);
15337
}
15338
if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
15339
settings->WantDelete = true;
15340
}
15341
15342
static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15343
{
15344
ImGuiContext& g = *ctx;
15345
for (ImGuiWindow* window : g.Windows)
15346
window->SettingsOffset = -1;
15347
g.SettingsWindows.clear();
15348
}
15349
15350
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15351
{
15352
ImGuiID id = ImHashStr(name);
15353
ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
15354
if (settings)
15355
*settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
15356
else
15357
settings = ImGui::CreateNewWindowSettings(name);
15358
settings->ID = id;
15359
settings->WantApply = true;
15360
return (void*)settings;
15361
}
15362
15363
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
15364
{
15365
ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
15366
int x, y;
15367
int i;
15368
if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
15369
else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
15370
else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
15371
else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); }
15372
}
15373
15374
// Apply to existing windows (if any)
15375
static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15376
{
15377
ImGuiContext& g = *ctx;
15378
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15379
if (settings->WantApply)
15380
{
15381
if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
15382
ApplyWindowSettings(window, settings);
15383
settings->WantApply = false;
15384
}
15385
}
15386
15387
static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15388
{
15389
// Gather data from windows that were active during this session
15390
// (if a window wasn't opened in this session we preserve its settings)
15391
ImGuiContext& g = *ctx;
15392
for (ImGuiWindow* window : g.Windows)
15393
{
15394
if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
15395
continue;
15396
15397
ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
15398
if (!settings)
15399
{
15400
settings = ImGui::CreateNewWindowSettings(window->Name);
15401
window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
15402
}
15403
IM_ASSERT(settings->ID == window->ID);
15404
settings->Pos = ImVec2ih(window->Pos);
15405
settings->Size = ImVec2ih(window->SizeFull);
15406
settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;
15407
settings->Collapsed = window->Collapsed;
15408
settings->WantDelete = false;
15409
}
15410
15411
// Write to text buffer
15412
buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
15413
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15414
{
15415
if (settings->WantDelete)
15416
continue;
15417
const char* settings_name = settings->GetName();
15418
buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
15419
if (settings->IsChild)
15420
{
15421
buf->appendf("IsChild=1\n");
15422
buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15423
}
15424
else
15425
{
15426
buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
15427
buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15428
if (settings->Collapsed)
15429
buf->appendf("Collapsed=1\n");
15430
}
15431
buf->append("\n");
15432
}
15433
}
15434
15435
//-----------------------------------------------------------------------------
15436
// [SECTION] LOCALIZATION
15437
//-----------------------------------------------------------------------------
15438
15439
void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
15440
{
15441
ImGuiContext& g = *GImGui;
15442
for (int n = 0; n < count; n++)
15443
g.LocalizationTable[entries[n].Key] = entries[n].Text;
15444
}
15445
15446
//-----------------------------------------------------------------------------
15447
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
15448
//-----------------------------------------------------------------------------
15449
// - GetMainViewport()
15450
// - SetWindowViewport() [Internal]
15451
// - ScaleWindowsInViewport() [Internal]
15452
// - UpdateViewportsNewFrame() [Internal]
15453
// (this section is more complete in the 'docking' branch)
15454
//-----------------------------------------------------------------------------
15455
15456
ImGuiViewport* ImGui::GetMainViewport()
15457
{
15458
ImGuiContext& g = *GImGui;
15459
return g.Viewports[0];
15460
}
15461
15462
void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
15463
{
15464
window->Viewport = viewport;
15465
}
15466
15467
static void ScaleWindow(ImGuiWindow* window, float scale)
15468
{
15469
ImVec2 origin = window->Viewport->Pos;
15470
window->Pos = ImFloor((window->Pos - origin) * scale + origin);
15471
window->Size = ImTrunc(window->Size * scale);
15472
window->SizeFull = ImTrunc(window->SizeFull * scale);
15473
window->ContentSize = ImTrunc(window->ContentSize * scale);
15474
}
15475
15476
// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
15477
void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
15478
{
15479
ImGuiContext& g = *GImGui;
15480
for (ImGuiWindow* window : g.Windows)
15481
if (window->Viewport == viewport)
15482
ScaleWindow(window, scale);
15483
}
15484
15485
// Update viewports and monitor infos
15486
static void ImGui::UpdateViewportsNewFrame()
15487
{
15488
ImGuiContext& g = *GImGui;
15489
IM_ASSERT(g.Viewports.Size == 1);
15490
15491
// Update main viewport with current platform position.
15492
// FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
15493
ImGuiViewportP* main_viewport = g.Viewports[0];
15494
main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
15495
main_viewport->Pos = ImVec2(0.0f, 0.0f);
15496
main_viewport->Size = g.IO.DisplaySize;
15497
main_viewport->FramebufferScale = g.IO.DisplayFramebufferScale;
15498
IM_ASSERT(main_viewport->FramebufferScale.x > 0.0f && main_viewport->FramebufferScale.y > 0.0f);
15499
15500
for (ImGuiViewportP* viewport : g.Viewports)
15501
{
15502
// Lock down space taken by menu bars and status bars
15503
// Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.
15504
viewport->WorkInsetMin = viewport->BuildWorkInsetMin;
15505
viewport->WorkInsetMax = viewport->BuildWorkInsetMax;
15506
viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f);
15507
viewport->UpdateWorkRect();
15508
}
15509
}
15510
15511
//-----------------------------------------------------------------------------
15512
// [SECTION] DOCKING
15513
//-----------------------------------------------------------------------------
15514
15515
// (this section is filled in the 'docking' branch)
15516
15517
15518
//-----------------------------------------------------------------------------
15519
// [SECTION] PLATFORM DEPENDENT HELPERS
15520
//-----------------------------------------------------------------------------
15521
// - Default clipboard handlers
15522
// - Default shell function handlers
15523
// - Default IME handlers
15524
//-----------------------------------------------------------------------------
15525
15526
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
15527
15528
#ifdef _MSC_VER
15529
#pragma comment(lib, "user32")
15530
#pragma comment(lib, "kernel32")
15531
#endif
15532
15533
// Win32 clipboard implementation
15534
// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
15535
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15536
{
15537
ImGuiContext& g = *ctx;
15538
g.ClipboardHandlerData.clear();
15539
if (!::OpenClipboard(NULL))
15540
return NULL;
15541
HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
15542
if (wbuf_handle == NULL)
15543
{
15544
::CloseClipboard();
15545
return NULL;
15546
}
15547
if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
15548
{
15549
int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
15550
g.ClipboardHandlerData.resize(buf_len);
15551
::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
15552
}
15553
::GlobalUnlock(wbuf_handle);
15554
::CloseClipboard();
15555
return g.ClipboardHandlerData.Data;
15556
}
15557
15558
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
15559
{
15560
if (!::OpenClipboard(NULL))
15561
return;
15562
const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
15563
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
15564
if (wbuf_handle == NULL)
15565
{
15566
::CloseClipboard();
15567
return;
15568
}
15569
WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
15570
::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
15571
::GlobalUnlock(wbuf_handle);
15572
::EmptyClipboard();
15573
if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
15574
::GlobalFree(wbuf_handle);
15575
::CloseClipboard();
15576
}
15577
15578
#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
15579
15580
#include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
15581
static PasteboardRef main_clipboard = 0;
15582
15583
// OSX clipboard implementation
15584
// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
15585
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
15586
{
15587
if (!main_clipboard)
15588
PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15589
PasteboardClear(main_clipboard);
15590
CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, ImStrlen(text));
15591
if (cf_data)
15592
{
15593
PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
15594
CFRelease(cf_data);
15595
}
15596
}
15597
15598
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15599
{
15600
ImGuiContext& g = *ctx;
15601
if (!main_clipboard)
15602
PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15603
PasteboardSynchronize(main_clipboard);
15604
15605
ItemCount item_count = 0;
15606
PasteboardGetItemCount(main_clipboard, &item_count);
15607
for (ItemCount i = 0; i < item_count; i++)
15608
{
15609
PasteboardItemID item_id = 0;
15610
PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
15611
CFArrayRef flavor_type_array = 0;
15612
PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
15613
for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
15614
{
15615
CFDataRef cf_data;
15616
if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
15617
{
15618
g.ClipboardHandlerData.clear();
15619
int length = (int)CFDataGetLength(cf_data);
15620
g.ClipboardHandlerData.resize(length + 1);
15621
CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
15622
g.ClipboardHandlerData[length] = 0;
15623
CFRelease(cf_data);
15624
return g.ClipboardHandlerData.Data;
15625
}
15626
}
15627
}
15628
return NULL;
15629
}
15630
15631
#else
15632
15633
// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
15634
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15635
{
15636
ImGuiContext& g = *ctx;
15637
return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
15638
}
15639
15640
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text)
15641
{
15642
ImGuiContext& g = *ctx;
15643
g.ClipboardHandlerData.clear();
15644
const char* text_end = text + ImStrlen(text);
15645
g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
15646
memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
15647
g.ClipboardHandlerData[(int)(text_end - text)] = 0;
15648
}
15649
15650
#endif // Default clipboard handlers
15651
15652
//-----------------------------------------------------------------------------
15653
15654
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15655
#if defined(__APPLE__) && TARGET_OS_IPHONE
15656
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15657
#endif
15658
#if defined(__3DS__)
15659
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15660
#endif
15661
#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
15662
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15663
#endif
15664
#endif // #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15665
15666
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15667
#ifdef _WIN32
15668
#include <shellapi.h> // ShellExecuteA()
15669
#ifdef _MSC_VER
15670
#pragma comment(lib, "shell32")
15671
#endif
15672
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
15673
{
15674
const int path_wsize = ::MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
15675
ImVector<wchar_t> path_wbuf;
15676
path_wbuf.resize(path_wsize);
15677
::MultiByteToWideChar(CP_UTF8, 0, path, -1, path_wbuf.Data, path_wsize);
15678
return (INT_PTR)::ShellExecuteW(NULL, L"open", path_wbuf.Data, NULL, NULL, SW_SHOWDEFAULT) > 32;
15679
}
15680
#else
15681
#include <sys/wait.h>
15682
#include <unistd.h>
15683
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
15684
{
15685
#if defined(__APPLE__)
15686
const char* args[] { "open", "--", path, NULL };
15687
#else
15688
const char* args[] { "xdg-open", path, NULL };
15689
#endif
15690
pid_t pid = fork();
15691
if (pid < 0)
15692
return false;
15693
if (!pid)
15694
{
15695
execvp(args[0], const_cast<char **>(args));
15696
exit(-1);
15697
}
15698
else
15699
{
15700
int status;
15701
waitpid(pid, &status, 0);
15702
return WEXITSTATUS(status) == 0;
15703
}
15704
}
15705
#endif
15706
#else
15707
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }
15708
#endif // Default shell handlers
15709
15710
//-----------------------------------------------------------------------------
15711
15712
// Win32 API IME support (for Asian languages, etc.)
15713
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
15714
15715
#include <imm.h>
15716
#ifdef _MSC_VER
15717
#pragma comment(lib, "imm32")
15718
#endif
15719
15720
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
15721
{
15722
// Notify OS Input Method Editor of text input position
15723
HWND hwnd = (HWND)viewport->PlatformHandleRaw;
15724
if (hwnd == 0)
15725
return;
15726
15727
//::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
15728
if (HIMC himc = ::ImmGetContext(hwnd))
15729
{
15730
COMPOSITIONFORM composition_form = {};
15731
composition_form.ptCurrentPos.x = (LONG)data->InputPos.x;
15732
composition_form.ptCurrentPos.y = (LONG)data->InputPos.y;
15733
composition_form.dwStyle = CFS_FORCE_POSITION;
15734
::ImmSetCompositionWindow(himc, &composition_form);
15735
CANDIDATEFORM candidate_form = {};
15736
candidate_form.dwStyle = CFS_CANDIDATEPOS;
15737
candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x;
15738
candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y;
15739
::ImmSetCandidateWindow(himc, &candidate_form);
15740
::ImmReleaseContext(hwnd, himc);
15741
}
15742
}
15743
15744
#else
15745
15746
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}
15747
15748
#endif // Default IME handlers
15749
15750
//-----------------------------------------------------------------------------
15751
// [SECTION] METRICS/DEBUGGER WINDOW
15752
//-----------------------------------------------------------------------------
15753
// - DebugRenderViewportThumbnail() [Internal]
15754
// - RenderViewportsThumbnails() [Internal]
15755
// - DebugTextEncoding()
15756
// - MetricsHelpMarker() [Internal]
15757
// - ShowFontAtlas() [Internal but called by Demo!]
15758
// - DebugNodeTexture() [Internal]
15759
// - ShowMetricsWindow()
15760
// - DebugNodeColumns() [Internal]
15761
// - DebugNodeDrawList() [Internal]
15762
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
15763
// - DebugNodeFont() [Internal]
15764
// - DebugNodeFontGlyph() [Internal]
15765
// - DebugNodeStorage() [Internal]
15766
// - DebugNodeTabBar() [Internal]
15767
// - DebugNodeViewport() [Internal]
15768
// - DebugNodeWindow() [Internal]
15769
// - DebugNodeWindowSettings() [Internal]
15770
// - DebugNodeWindowsList() [Internal]
15771
// - DebugNodeWindowsListByBeginStackParent() [Internal]
15772
//-----------------------------------------------------------------------------
15773
15774
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
15775
15776
void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
15777
{
15778
ImGuiContext& g = *GImGui;
15779
ImGuiWindow* window = g.CurrentWindow;
15780
15781
ImVec2 scale = bb.GetSize() / viewport->Size;
15782
ImVec2 off = bb.Min - viewport->Pos * scale;
15783
float alpha_mul = 1.0f;
15784
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
15785
for (ImGuiWindow* thumb_window : g.Windows)
15786
{
15787
if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
15788
continue;
15789
15790
ImRect thumb_r = thumb_window->Rect();
15791
ImRect title_r = thumb_window->TitleBarRect();
15792
thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale));
15793
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
15794
thumb_r.ClipWithFull(bb);
15795
title_r.ClipWithFull(bb);
15796
const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
15797
window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
15798
window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
15799
window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
15800
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));
15801
}
15802
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
15803
if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
15804
window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
15805
}
15806
15807
static void RenderViewportsThumbnails()
15808
{
15809
ImGuiContext& g = *GImGui;
15810
ImGuiWindow* window = g.CurrentWindow;
15811
15812
float SCALE = 1.0f / 8.0f;
15813
ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size);
15814
ImVec2 p = window->DC.CursorPos;
15815
ImVec2 off = p - bb_full.Min * SCALE;
15816
15817
// Draw viewports
15818
for (ImGuiViewportP* viewport : g.Viewports)
15819
{
15820
ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
15821
ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
15822
}
15823
ImGui::Dummy(bb_full.GetSize() * SCALE);
15824
}
15825
15826
// Draw an arbitrary US keyboard layout to visualize translated keys
15827
void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
15828
{
15829
const float scale = ImGui::GetFontSize() / 13.0f;
15830
const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
15831
const float key_rounding = 3.0f * scale;
15832
const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
15833
const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
15834
const float key_face_rounding = 2.0f * scale;
15835
const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
15836
const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
15837
const float key_row_offset = 9.0f * scale;
15838
15839
ImVec2 board_min = GetCursorScreenPos();
15840
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);
15841
ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
15842
15843
struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
15844
const KeyLayoutData keys_to_display[] =
15845
{
15846
{ 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
15847
{ 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
15848
{ 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
15849
};
15850
15851
// Elements rendered manually via ImDrawList API are not clipped automatically.
15852
// While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
15853
Dummy(board_max - board_min);
15854
if (!IsItemVisible())
15855
return;
15856
draw_list->PushClipRect(board_min, board_max, true);
15857
for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
15858
{
15859
const KeyLayoutData* key_data = &keys_to_display[n];
15860
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);
15861
ImVec2 key_max = key_min + key_size;
15862
draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
15863
draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
15864
ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
15865
ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
15866
draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
15867
draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
15868
ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
15869
draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
15870
if (IsKeyDown(key_data->Key))
15871
draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
15872
}
15873
draw_list->PopClipRect();
15874
}
15875
15876
// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
15877
void ImGui::DebugTextEncoding(const char* str)
15878
{
15879
Text("Text: \"%s\"", str);
15880
if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
15881
return;
15882
TableSetupColumn("Offset");
15883
TableSetupColumn("UTF-8");
15884
TableSetupColumn("Glyph");
15885
TableSetupColumn("Codepoint");
15886
TableHeadersRow();
15887
for (const char* p = str; *p != 0; )
15888
{
15889
unsigned int c;
15890
const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
15891
TableNextColumn();
15892
Text("%d", (int)(p - str));
15893
TableNextColumn();
15894
for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
15895
{
15896
if (byte_index > 0)
15897
SameLine();
15898
Text("0x%02X", (int)(unsigned char)p[byte_index]);
15899
}
15900
TableNextColumn();
15901
TextUnformatted(p, p + c_utf8_len);
15902
if (!GetFont()->IsGlyphInFont((ImWchar)c))
15903
{
15904
SameLine();
15905
TextUnformatted("[missing]");
15906
}
15907
TableNextColumn();
15908
Text("U+%04X", (int)c);
15909
p += c_utf8_len;
15910
}
15911
EndTable();
15912
}
15913
15914
static void DebugFlashStyleColorStop()
15915
{
15916
ImGuiContext& g = *GImGui;
15917
if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
15918
g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
15919
g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
15920
}
15921
15922
// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
15923
void ImGui::DebugFlashStyleColor(ImGuiCol idx)
15924
{
15925
ImGuiContext& g = *GImGui;
15926
DebugFlashStyleColorStop();
15927
g.DebugFlashStyleColorTime = 0.5f;
15928
g.DebugFlashStyleColorIdx = idx;
15929
g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
15930
}
15931
15932
void ImGui::UpdateDebugToolFlashStyleColor()
15933
{
15934
ImGuiContext& g = *GImGui;
15935
if (g.DebugFlashStyleColorTime <= 0.0f)
15936
return;
15937
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);
15938
g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
15939
if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
15940
DebugFlashStyleColorStop();
15941
}
15942
15943
static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
15944
{
15945
union { void* ptr; int integer; } tex_id_opaque;
15946
memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
15947
if (sizeof(tex_id) >= sizeof(void*))
15948
ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
15949
else
15950
ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
15951
return buf;
15952
}
15953
15954
static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, const ImDrawCmd* cmd)
15955
{
15956
char* buf_end = buf + buf_size;
15957
if (cmd->TexRef._TexData != NULL)
15958
buf += ImFormatString(buf, buf_end - buf, "#%03d: ", cmd->TexRef._TexData->UniqueID);
15959
return FormatTextureIDForDebugDisplay(buf, (int)(buf_end - buf), cmd->TexRef.GetTexID()); // Calling TexRef::GetTexID() to avoid assert of cmd->GetTexID()
15960
}
15961
15962
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
15963
static void MetricsHelpMarker(const char* desc)
15964
{
15965
ImGui::TextDisabled("(?)");
15966
if (ImGui::BeginItemTooltip())
15967
{
15968
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
15969
ImGui::TextUnformatted(desc);
15970
ImGui::PopTextWrapPos();
15971
ImGui::EndTooltip();
15972
}
15973
}
15974
15975
#ifdef IMGUI_ENABLE_FREETYPE
15976
namespace ImGuiFreeType { IMGUI_API const ImFontLoader* GetFontLoader(); IMGUI_API bool DebugEditFontLoaderFlags(unsigned int* p_font_builder_flags); }
15977
#endif
15978
15979
// [DEBUG] List fonts in a font atlas and display its texture
15980
void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
15981
{
15982
ImGuiContext& g = *GImGui;
15983
ImGuiIO& io = g.IO;
15984
ImGuiStyle& style = g.Style;
15985
15986
BeginDisabled();
15987
CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
15988
EndDisabled();
15989
ShowFontSelector("Font");
15990
//BeginDisabled((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0);
15991
if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
15992
style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
15993
SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
15994
SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later.");
15995
DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 5.0f);
15996
//BeginDisabled(io.ConfigDpiScaleFonts);
15997
DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 5.0f);
15998
//SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
15999
//EndDisabled();
16000
if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
16001
BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
16002
BulletText("Load a nice font for better results!");
16003
BulletText("Please submit feedback:");
16004
SameLine(); TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465");
16005
BulletText("Read FAQ for more details:");
16006
SameLine(); TextLinkOpenURL("dearimgui.com/faq", "https://www.dearimgui.com/faq/");
16007
//EndDisabled();
16008
16009
SeparatorText("Font List");
16010
16011
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16012
Checkbox("Show font preview", &cfg->ShowFontPreview);
16013
16014
// Font loaders
16015
if (TreeNode("Loader", "Loader: \'%s\'", atlas->FontLoaderName ? atlas->FontLoaderName : "NULL"))
16016
{
16017
const ImFontLoader* loader_current = atlas->FontLoader;
16018
BeginDisabled(!atlas->RendererHasTextures);
16019
#ifdef IMGUI_ENABLE_STB_TRUETYPE
16020
const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype();
16021
if (RadioButton("stb_truetype", loader_current == loader_stbtruetype))
16022
ImFontAtlasBuildSetupFontLoader(atlas, loader_stbtruetype);
16023
#else
16024
BeginDisabled();
16025
RadioButton("stb_truetype", false);
16026
SetItemTooltip("Requires #define IMGUI_ENABLE_STB_TRUETYPE");
16027
EndDisabled();
16028
#endif
16029
SameLine();
16030
#ifdef IMGUI_ENABLE_FREETYPE
16031
const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader();
16032
if (RadioButton("FreeType", loader_current == loader_freetype))
16033
ImFontAtlasBuildSetupFontLoader(atlas, loader_freetype);
16034
if (loader_current == loader_freetype)
16035
{
16036
unsigned int loader_flags = atlas->FontLoaderFlags;
16037
Text("Shared FreeType Loader Flags: 0x%08X", loader_flags);
16038
if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))
16039
{
16040
for (ImFont* font : atlas->Fonts)
16041
ImFontAtlasFontDestroyOutput(atlas, font);
16042
atlas->FontLoaderFlags = loader_flags;
16043
for (ImFont* font : atlas->Fonts)
16044
ImFontAtlasFontInitOutput(atlas, font);
16045
}
16046
}
16047
#else
16048
BeginDisabled();
16049
RadioButton("FreeType", false);
16050
SetItemTooltip("Requires #define IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp.");
16051
EndDisabled();
16052
#endif
16053
EndDisabled();
16054
TreePop();
16055
}
16056
16057
// Font list
16058
for (ImFont* font : atlas->Fonts)
16059
{
16060
PushID(font);
16061
DebugNodeFont(font);
16062
PopID();
16063
}
16064
16065
SeparatorText("Font Atlas");
16066
if (Button("Compact"))
16067
atlas->CompactCache();
16068
SameLine();
16069
if (Button("Grow"))
16070
ImFontAtlasTextureGrow(atlas);
16071
SameLine();
16072
if (Button("Clear All"))
16073
ImFontAtlasBuildClear(atlas);
16074
SetItemTooltip("Destroy cache and custom rectangles.");
16075
16076
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
16077
{
16078
ImTextureData* tex = atlas->TexList[tex_n];
16079
if (tex_n > 0)
16080
SameLine();
16081
Text("Tex: %dx%d", tex->Width, tex->Height);
16082
}
16083
const int packed_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsPackedSurface);
16084
const int discarded_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsDiscardedSurface);
16085
Text("Packed rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsPackedCount, atlas->Builder->RectsPackedSurface, packed_surface_sqrt, packed_surface_sqrt);
16086
Text("incl. Discarded rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsDiscardedCount, atlas->Builder->RectsDiscardedSurface, discarded_surface_sqrt, discarded_surface_sqrt);
16087
16088
ImFontAtlasRectId highlight_r_id = ImFontAtlasRectId_Invalid;
16089
if (TreeNode("Rects Index", "Rects Index (%d)", atlas->Builder->RectsPackedCount)) // <-- Use count of used rectangles
16090
{
16091
PushStyleVar(ImGuiStyleVar_ImageBorderSize, 1.0f);
16092
if (BeginTable("##table", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY, ImVec2(0.0f, GetTextLineHeightWithSpacing() * 12)))
16093
{
16094
for (const ImFontAtlasRectEntry& entry : atlas->Builder->RectsIndex)
16095
if (entry.IsUsed)
16096
{
16097
ImFontAtlasRectId id = ImFontAtlasRectId_Make(atlas->Builder->RectsIndex.index_from_ptr(&entry), entry.Generation);
16098
ImFontAtlasRect r = {};
16099
atlas->GetCustomRect(id, &r);
16100
const char* buf;
16101
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);
16102
TableNextColumn();
16103
Selectable(buf);
16104
if (IsItemHovered())
16105
highlight_r_id = id;
16106
TableNextColumn();
16107
Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);
16108
}
16109
EndTable();
16110
}
16111
PopStyleVar();
16112
TreePop();
16113
}
16114
16115
// Texture list
16116
// (ensure the last texture always use the same ID, so we can keep it open neatly)
16117
ImFontAtlasRect highlight_r;
16118
if (highlight_r_id != ImFontAtlasRectId_Invalid)
16119
atlas->GetCustomRect(highlight_r_id, &highlight_r);
16120
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
16121
{
16122
if (tex_n == atlas->TexList.Size - 1)
16123
SetNextItemOpen(true, ImGuiCond_Once);
16124
DebugNodeTexture(atlas->TexList[tex_n], atlas->TexList.Size - 1 - tex_n, (highlight_r_id != ImFontAtlasRectId_Invalid) ? &highlight_r : NULL);
16125
}
16126
}
16127
16128
void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect)
16129
{
16130
ImGuiContext& g = *GImGui;
16131
PushID(int_id);
16132
if (TreeNode("", "Texture #%03d (%dx%d pixels)", tex->UniqueID, tex->Width, tex->Height))
16133
{
16134
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16135
Checkbox("Show used rect", &cfg->ShowTextureUsedRect);
16136
PushStyleVar(ImGuiStyleVar_ImageBorderSize, ImMax(1.0f, g.Style.ImageBorderSize));
16137
ImVec2 p = GetCursorScreenPos();
16138
if (tex->WantDestroyNextFrame)
16139
Dummy(ImVec2((float)tex->Width, (float)tex->Height));
16140
else
16141
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));
16142
if (cfg->ShowTextureUsedRect)
16143
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));
16144
if (highlight_rect != NULL)
16145
{
16146
ImRect r_outer(p.x, p.y, p.x + tex->Width, p.y + tex->Height);
16147
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);
16148
RenderRectFilledWithHole(GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0f);
16149
GetWindowDrawList()->AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255));
16150
}
16151
PopStyleVar();
16152
16153
char texid_desc[30];
16154
Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors);
16155
Text("TexID = %s, BackendUserData = %p", FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), tex->TexID), tex->BackendUserData);
16156
TreePop();
16157
}
16158
PopID();
16159
}
16160
16161
void ImGui::ShowMetricsWindow(bool* p_open)
16162
{
16163
ImGuiContext& g = *GImGui;
16164
ImGuiIO& io = g.IO;
16165
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16166
if (cfg->ShowDebugLog)
16167
ShowDebugLogWindow(&cfg->ShowDebugLog);
16168
if (cfg->ShowIDStackTool)
16169
ShowIDStackToolWindow(&cfg->ShowIDStackTool);
16170
16171
if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
16172
{
16173
End();
16174
return;
16175
}
16176
16177
// [DEBUG] Clear debug breaks hooks after exactly one cycle.
16178
DebugBreakClearData();
16179
16180
// Basic info
16181
Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
16182
if (g.ContextName[0] != 0)
16183
{
16184
SameLine();
16185
Text("(Context Name: \"%s\")", g.ContextName);
16186
}
16187
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
16188
Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
16189
Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
16190
//SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
16191
16192
Separator();
16193
16194
// Debugging enums
16195
enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
16196
const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
16197
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
16198
const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
16199
if (cfg->ShowWindowsRectsType < 0)
16200
cfg->ShowWindowsRectsType = WRT_WorkRect;
16201
if (cfg->ShowTablesRectsType < 0)
16202
cfg->ShowTablesRectsType = TRT_WorkRect;
16203
16204
struct Funcs
16205
{
16206
static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
16207
{
16208
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
16209
if (rect_type == TRT_OuterRect) { return table->OuterRect; }
16210
else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
16211
else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
16212
else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
16213
else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
16214
else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
16215
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); }
16216
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); }
16217
else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
16218
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
16219
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); }
16220
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); }
16221
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); }
16222
IM_ASSERT(0);
16223
return ImRect();
16224
}
16225
16226
static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
16227
{
16228
if (rect_type == WRT_OuterRect) { return window->Rect(); }
16229
else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
16230
else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
16231
else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
16232
else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
16233
else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
16234
else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
16235
else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
16236
IM_ASSERT(0);
16237
return ImRect();
16238
}
16239
};
16240
16241
// Tools
16242
if (TreeNode("Tools"))
16243
{
16244
// Debug Break features
16245
// The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
16246
SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
16247
SameLine();
16248
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.");
16249
if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
16250
DebugStartItemPicker();
16251
Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
16252
16253
SeparatorText("Visualize");
16254
16255
Checkbox("Show Debug Log", &cfg->ShowDebugLog);
16256
SameLine();
16257
MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
16258
16259
Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
16260
SameLine();
16261
MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
16262
16263
Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
16264
Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
16265
SameLine();
16266
SetNextItemWidth(GetFontSize() * 12);
16267
cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
16268
if (cfg->ShowWindowsRects && g.NavWindow != NULL)
16269
{
16270
BulletText("'%s':", g.NavWindow->Name);
16271
Indent();
16272
for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
16273
{
16274
ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
16275
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]);
16276
}
16277
Unindent();
16278
}
16279
16280
Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
16281
SameLine();
16282
SetNextItemWidth(GetFontSize() * 12);
16283
cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
16284
if (cfg->ShowTablesRects && g.NavWindow != NULL)
16285
{
16286
for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16287
{
16288
ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16289
if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
16290
continue;
16291
16292
BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
16293
if (IsItemHovered())
16294
GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16295
Indent();
16296
char buf[128];
16297
for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
16298
{
16299
if (rect_n >= TRT_ColumnsRect)
16300
{
16301
if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
16302
continue;
16303
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16304
{
16305
ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
16306
ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
16307
Selectable(buf);
16308
if (IsItemHovered())
16309
GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16310
}
16311
}
16312
else
16313
{
16314
ImRect r = Funcs::GetTableRect(table, rect_n, -1);
16315
ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
16316
Selectable(buf);
16317
if (IsItemHovered())
16318
GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16319
}
16320
}
16321
Unindent();
16322
}
16323
}
16324
Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
16325
16326
SeparatorText("Validate");
16327
16328
Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
16329
SameLine();
16330
MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
16331
16332
Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
16333
SameLine();
16334
MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
16335
if (cfg->ShowTextEncodingViewer)
16336
{
16337
static char buf[64] = "";
16338
SetNextItemWidth(-FLT_MIN);
16339
InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
16340
if (buf[0] != 0)
16341
DebugTextEncoding(buf);
16342
}
16343
16344
TreePop();
16345
}
16346
16347
// Windows
16348
if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
16349
{
16350
//SetNextItemOpen(true, ImGuiCond_Once);
16351
DebugNodeWindowsList(&g.Windows, "By display order");
16352
DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
16353
if (TreeNode("By submission order (begin stack)"))
16354
{
16355
// Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
16356
ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
16357
temp_buffer.resize(0);
16358
for (ImGuiWindow* window : g.Windows)
16359
if (window->LastFrameActive + 1 >= g.FrameCount)
16360
temp_buffer.push_back(window);
16361
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); } };
16362
ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
16363
DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
16364
TreePop();
16365
}
16366
16367
TreePop();
16368
}
16369
16370
// DrawLists
16371
int drawlist_count = 0;
16372
for (ImGuiViewportP* viewport : g.Viewports)
16373
drawlist_count += viewport->DrawDataP.CmdLists.Size;
16374
if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
16375
{
16376
Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
16377
Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
16378
for (ImGuiViewportP* viewport : g.Viewports)
16379
for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
16380
DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
16381
TreePop();
16382
}
16383
16384
// Viewports
16385
if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
16386
{
16387
SetNextItemOpen(true, ImGuiCond_Once);
16388
if (TreeNode("Windows Minimap"))
16389
{
16390
RenderViewportsThumbnails();
16391
TreePop();
16392
}
16393
cfg->HighlightViewportID = 0;
16394
16395
for (ImGuiViewportP* viewport : g.Viewports)
16396
DebugNodeViewport(viewport);
16397
TreePop();
16398
}
16399
16400
// Details for Fonts
16401
for (ImFontAtlas* atlas : g.FontAtlases)
16402
if (TreeNode((void*)atlas, "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size))
16403
{
16404
ShowFontAtlas(atlas);
16405
TreePop();
16406
}
16407
16408
// Details for Popups
16409
if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
16410
{
16411
for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
16412
{
16413
// As it's difficult to interact with tree nodes while popups are open, we display everything inline.
16414
ImGuiWindow* window = popup_data.Window;
16415
BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
16416
popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
16417
popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
16418
}
16419
TreePop();
16420
}
16421
16422
// Details for TabBars
16423
if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
16424
{
16425
for (int n = 0; n < g.TabBars.GetMapSize(); n++)
16426
if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
16427
{
16428
PushID(tab_bar);
16429
DebugNodeTabBar(tab_bar, "TabBar");
16430
PopID();
16431
}
16432
TreePop();
16433
}
16434
16435
// Details for Tables
16436
if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
16437
{
16438
for (int n = 0; n < g.Tables.GetMapSize(); n++)
16439
if (ImGuiTable* table = g.Tables.TryGetMapData(n))
16440
DebugNodeTable(table);
16441
TreePop();
16442
}
16443
16444
// Details for InputText
16445
if (TreeNode("InputText"))
16446
{
16447
DebugNodeInputTextState(&g.InputTextState);
16448
TreePop();
16449
}
16450
16451
// Details for TypingSelect
16452
if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
16453
{
16454
DebugNodeTypingSelectState(&g.TypingSelectState);
16455
TreePop();
16456
}
16457
16458
// Details for MultiSelect
16459
if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
16460
{
16461
ImGuiBoxSelectState* bs = &g.BoxSelectState;
16462
BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
16463
for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
16464
if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
16465
DebugNodeMultiSelectState(state);
16466
TreePop();
16467
}
16468
16469
// Details for Docking
16470
#ifdef IMGUI_HAS_DOCK
16471
if (TreeNode("Docking"))
16472
{
16473
TreePop();
16474
}
16475
#endif // #ifdef IMGUI_HAS_DOCK
16476
16477
// Settings
16478
if (TreeNode("Settings"))
16479
{
16480
if (SmallButton("Clear"))
16481
ClearIniSettings();
16482
SameLine();
16483
if (SmallButton("Save to memory"))
16484
SaveIniSettingsToMemory();
16485
SameLine();
16486
if (SmallButton("Save to disk"))
16487
SaveIniSettingsToDisk(g.IO.IniFilename);
16488
SameLine();
16489
if (g.IO.IniFilename)
16490
Text("\"%s\"", g.IO.IniFilename);
16491
else
16492
TextUnformatted("<NULL>");
16493
Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
16494
Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
16495
if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
16496
{
16497
for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
16498
BulletText("\"%s\"", handler.TypeName);
16499
TreePop();
16500
}
16501
if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
16502
{
16503
for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16504
DebugNodeWindowSettings(settings);
16505
TreePop();
16506
}
16507
16508
if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
16509
{
16510
for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
16511
DebugNodeTableSettings(settings);
16512
TreePop();
16513
}
16514
16515
#ifdef IMGUI_HAS_DOCK
16516
#endif // #ifdef IMGUI_HAS_DOCK
16517
16518
if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
16519
{
16520
InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
16521
TreePop();
16522
}
16523
TreePop();
16524
}
16525
16526
// Settings
16527
if (TreeNode("Memory allocations"))
16528
{
16529
ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
16530
Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
16531
if (SmallButton("GC now")) { g.GcCompactAll = true; }
16532
Text("Recent frames with allocations:");
16533
int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
16534
for (int n = buf_size - 1; n >= 0; n--)
16535
{
16536
ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
16537
BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);
16538
if (n == 0)
16539
{
16540
SameLine();
16541
Text("<- %d frames ago", g.FrameCount - entry->FrameCount);
16542
}
16543
}
16544
TreePop();
16545
}
16546
16547
if (TreeNode("Inputs"))
16548
{
16549
Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
16550
{
16551
// User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
16552
Indent();
16553
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); }
16554
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); }
16555
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); }
16556
Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
16557
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.
16558
DebugRenderKeyboardPreview(GetWindowDrawList());
16559
Unindent();
16560
}
16561
16562
Text("MOUSE STATE");
16563
{
16564
Indent();
16565
if (IsMousePosValid())
16566
Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
16567
else
16568
Text("Mouse pos: <INVALID>");
16569
Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
16570
int count = IM_ARRAYSIZE(io.MouseDown);
16571
Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
16572
Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
16573
Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
16574
Text("Mouse wheel: %.1f", io.MouseWheel);
16575
Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
16576
Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
16577
Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
16578
Unindent();
16579
}
16580
16581
Text("MOUSE WHEELING");
16582
{
16583
Indent();
16584
Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
16585
Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
16586
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>");
16587
Unindent();
16588
}
16589
16590
Text("KEY OWNERS");
16591
{
16592
Indent();
16593
if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
16594
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
16595
{
16596
ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
16597
if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
16598
continue;
16599
Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
16600
owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
16601
DebugLocateItemOnHover(owner_data->OwnerCurr);
16602
}
16603
EndChild();
16604
Unindent();
16605
}
16606
Text("SHORTCUT ROUTING");
16607
SameLine();
16608
MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
16609
{
16610
Indent();
16611
if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
16612
for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
16613
{
16614
ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
16615
for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
16616
{
16617
ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
16618
ImGuiKeyChord key_chord = key | routing_data->Mods;
16619
Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
16620
DebugLocateItemOnHover(routing_data->RoutingCurr);
16621
if (g.IO.ConfigDebugIsDebuggerPresent)
16622
{
16623
SameLine();
16624
if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
16625
g.DebugBreakInShortcutRouting = key_chord;
16626
}
16627
idx = routing_data->NextEntryIndex;
16628
}
16629
}
16630
EndChild();
16631
Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
16632
Unindent();
16633
}
16634
TreePop();
16635
}
16636
16637
if (TreeNode("Internal state"))
16638
{
16639
Text("WINDOWING");
16640
Indent();
16641
Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
16642
Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
16643
Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
16644
Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
16645
Unindent();
16646
16647
Text("ITEMS");
16648
Indent();
16649
Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
16650
DebugLocateItemOnHover(g.ActiveId);
16651
Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
16652
Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
16653
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
16654
Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
16655
Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
16656
DebugLocateItemOnHover(g.DragDropPayload.SourceId);
16657
Unindent();
16658
16659
Text("NAV,FOCUS");
16660
Indent();
16661
Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
16662
Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
16663
DebugLocateItemOnHover(g.NavId);
16664
Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
16665
Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
16666
Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
16667
Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
16668
Text("NavActivateFlags: %04X", g.NavActivateFlags);
16669
Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav);
16670
Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
16671
Text("NavFocusRoute[] = ");
16672
for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
16673
{
16674
const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
16675
SameLine(0.0f, 0.0f);
16676
Text("0x%08X/", focus_scope.ID);
16677
SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
16678
}
16679
Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
16680
Unindent();
16681
16682
TreePop();
16683
}
16684
16685
// Overlay: Display windows Rectangles and Begin Order
16686
if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
16687
{
16688
for (ImGuiWindow* window : g.Windows)
16689
{
16690
if (!window->WasActive)
16691
continue;
16692
ImDrawList* draw_list = GetForegroundDrawList(window);
16693
if (cfg->ShowWindowsRects)
16694
{
16695
ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
16696
draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16697
}
16698
if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
16699
{
16700
char buf[32];
16701
ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
16702
float font_size = GetFontSize();
16703
draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
16704
draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
16705
}
16706
}
16707
}
16708
16709
// Overlay: Display Tables Rectangles
16710
if (cfg->ShowTablesRects)
16711
{
16712
for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16713
{
16714
ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16715
if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
16716
continue;
16717
ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
16718
if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
16719
{
16720
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16721
{
16722
ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
16723
ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
16724
float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
16725
draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
16726
}
16727
}
16728
else
16729
{
16730
ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
16731
draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16732
}
16733
}
16734
}
16735
16736
#ifdef IMGUI_HAS_DOCK
16737
// Overlay: Display Docking info
16738
if (show_docking_nodes && g.IO.KeyCtrl)
16739
{
16740
}
16741
#endif // #ifdef IMGUI_HAS_DOCK
16742
16743
End();
16744
}
16745
16746
void ImGui::DebugBreakClearData()
16747
{
16748
// Those fields are scattered in their respective subsystem to stay in hot-data locations
16749
ImGuiContext& g = *GImGui;
16750
g.DebugBreakInWindow = 0;
16751
g.DebugBreakInTable = 0;
16752
g.DebugBreakInShortcutRouting = ImGuiKey_None;
16753
}
16754
16755
void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
16756
{
16757
if (!BeginItemTooltip())
16758
return;
16759
Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
16760
Separator();
16761
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.");
16762
Separator();
16763
TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
16764
EndTooltip();
16765
}
16766
16767
// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
16768
// In order to reduce interferences with the contents we are trying to debug into.
16769
bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
16770
{
16771
ImGuiWindow* window = GetCurrentWindow();
16772
if (window->SkipItems)
16773
return false;
16774
16775
ImGuiContext& g = *GImGui;
16776
const ImGuiID id = window->GetID(label);
16777
const ImVec2 label_size = CalcTextSize(label, NULL, true);
16778
ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
16779
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
16780
16781
const ImRect bb(pos, pos + size);
16782
ItemSize(size, 0.0f);
16783
if (!ItemAdd(bb, id))
16784
return false;
16785
16786
// WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
16787
bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
16788
bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
16789
DebugBreakButtonTooltip(false, description_of_location);
16790
16791
ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
16792
ImVec4 hsv;
16793
ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
16794
ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
16795
16796
RenderNavCursor(bb, id);
16797
RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
16798
RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
16799
16800
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
16801
return pressed;
16802
}
16803
16804
// [DEBUG] Display contents of Columns
16805
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
16806
{
16807
if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
16808
return;
16809
BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
16810
for (ImGuiOldColumnData& column : columns->Columns)
16811
BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
16812
TreePop();
16813
}
16814
16815
// [DEBUG] Display contents of ImDrawList
16816
void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
16817
{
16818
ImGuiContext& g = *GImGui;
16819
IM_UNUSED(viewport); // Used in docking branch
16820
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16821
int cmd_count = draw_list->CmdBuffer.Size;
16822
if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
16823
cmd_count--;
16824
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);
16825
if (draw_list == GetWindowDrawList())
16826
{
16827
SameLine();
16828
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)
16829
if (node_open)
16830
TreePop();
16831
return;
16832
}
16833
16834
ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
16835
if (window && IsItemHovered() && fg_draw_list)
16836
fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
16837
if (!node_open)
16838
return;
16839
16840
if (window && !window->WasActive)
16841
TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
16842
16843
for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
16844
{
16845
if (pcmd->UserCallback)
16846
{
16847
BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
16848
continue;
16849
}
16850
16851
char texid_desc[30];
16852
FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd);
16853
char buf[300];
16854
ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
16855
pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
16856
bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
16857
if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
16858
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
16859
if (!pcmd_node_open)
16860
continue;
16861
16862
// Calculate approximate coverage area (touched pixel count)
16863
// This will be in pixels squared as long there's no post-scaling happening to the renderer output.
16864
const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
16865
const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
16866
float total_area = 0.0f;
16867
for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
16868
{
16869
ImVec2 triangle[3];
16870
for (int n = 0; n < 3; n++, idx_n++)
16871
triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
16872
total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
16873
}
16874
16875
// Display vertex information summary. Hover to get all triangles drawn in wire-frame
16876
ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
16877
Selectable(buf);
16878
if (IsItemHovered() && fg_draw_list)
16879
DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
16880
16881
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
16882
ImGuiListClipper clipper;
16883
clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
16884
while (clipper.Step())
16885
for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
16886
{
16887
char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
16888
ImVec2 triangle[3];
16889
for (int n = 0; n < 3; n++, idx_i++)
16890
{
16891
const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
16892
triangle[n] = v.pos;
16893
buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
16894
(n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
16895
}
16896
16897
Selectable(buf, false);
16898
if (fg_draw_list && IsItemHovered())
16899
{
16900
ImDrawListFlags backup_flags = fg_draw_list->Flags;
16901
fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
16902
fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
16903
fg_draw_list->Flags = backup_flags;
16904
}
16905
}
16906
TreePop();
16907
}
16908
TreePop();
16909
}
16910
16911
// [DEBUG] Display mesh/aabb of a ImDrawCmd
16912
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
16913
{
16914
IM_ASSERT(show_mesh || show_aabb);
16915
16916
// Draw wire-frame version of all triangles
16917
ImRect clip_rect = draw_cmd->ClipRect;
16918
ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
16919
ImDrawListFlags backup_flags = out_draw_list->Flags;
16920
out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
16921
for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
16922
{
16923
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
16924
ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
16925
16926
ImVec2 triangle[3];
16927
for (int n = 0; n < 3; n++, idx_n++)
16928
vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
16929
if (show_mesh)
16930
out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
16931
}
16932
// Draw bounding boxes
16933
if (show_aabb)
16934
{
16935
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
16936
out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
16937
}
16938
out_draw_list->Flags = backup_flags;
16939
}
16940
16941
// [DEBUG] Compute mask of inputs with the same codepoint.
16942
static int CalcFontGlyphSrcOverlapMask(ImFontAtlas* atlas, ImFont* font, unsigned int codepoint)
16943
{
16944
int mask = 0, count = 0;
16945
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
16946
{
16947
ImFontConfig* src = font->Sources[src_n];
16948
if (!(src->FontLoader ? src->FontLoader : atlas->FontLoader)->FontSrcContainsGlyph(atlas, src, (ImWchar)codepoint))
16949
continue;
16950
mask |= (1 << src_n);
16951
count++;
16952
}
16953
return count > 1 ? mask : 0;
16954
}
16955
16956
// [DEBUG] Display details for a single font, called by ShowStyleEditor().
16957
void ImGui::DebugNodeFont(ImFont* font)
16958
{
16959
ImGuiContext& g = *GImGui;
16960
ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16961
ImFontAtlas* atlas = font->ContainerAtlas;
16962
bool opened = TreeNode(font, "Font: \"%s\": %d sources(s)", font->GetDebugName(), font->Sources.Size);
16963
16964
// Display preview text
16965
if (!opened)
16966
Indent();
16967
Indent();
16968
if (cfg->ShowFontPreview)
16969
{
16970
PushFont(font, 0.0f, -1.0f);
16971
Text("The quick brown fox jumps over the lazy dog");
16972
PopFont();
16973
}
16974
if (!opened)
16975
{
16976
Unindent();
16977
Unindent();
16978
return;
16979
}
16980
if (SmallButton("Set as default"))
16981
GetIO().FontDefault = font;
16982
SameLine();
16983
BeginDisabled(atlas->Fonts.Size <= 1 || atlas->Locked);
16984
if (SmallButton("Remove"))
16985
atlas->RemoveFont(font);
16986
EndDisabled();
16987
SameLine();
16988
if (SmallButton("Clear bakes"))
16989
ImFontAtlasFontDiscardBakes(atlas, font, 0);
16990
SameLine();
16991
if (SmallButton("Clear unused"))
16992
ImFontAtlasFontDiscardBakes(atlas, font, 2);
16993
16994
// Display details
16995
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
16996
SetNextItemWidth(GetFontSize() * 8);
16997
DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
16998
/*SameLine(); MetricsHelpMarker(
16999
"Note that the default embedded font is NOT meant to be scaled.\n\n"
17000
"Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
17001
"You may oversample them to get some flexibility with scaling. "
17002
"You can also render at multiple sizes and select which one to use at runtime.\n\n"
17003
"(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");*/
17004
#endif
17005
17006
char c_str[5];
17007
Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
17008
Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
17009
17010
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17011
{
17012
ImFontConfig* src = font->Sources[src_n];
17013
if (TreeNode(src, "Input %d: \'%s\', Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)",
17014
src_n, src->Name, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y))
17015
{
17016
const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
17017
Text("Loader: '%s'", loader->Name ? loader->Name : "N/A");
17018
#ifdef IMGUI_ENABLE_FREETYPE
17019
if (loader->Name != NULL && strcmp(loader->Name, "FreeType") == 0)
17020
{
17021
unsigned int loader_flags = src->FontLoaderFlags;
17022
Text("FreeType Loader Flags: 0x%08X", loader_flags);
17023
if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))
17024
{
17025
ImFontAtlasFontDestroyOutput(atlas, font);
17026
src->FontLoaderFlags = loader_flags;
17027
ImFontAtlasFontInitOutput(atlas, font);
17028
}
17029
}
17030
#endif
17031
TreePop();
17032
}
17033
}
17034
if (font->Sources.Size > 1 && TreeNode("Input Glyphs Overlap Detection Tool"))
17035
{
17036
TextWrapped("- First Input that contains the glyph is used.\n"
17037
"- 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!");
17038
if (BeginTable("table", 2))
17039
{
17040
for (unsigned int c = 0; c < 0x10000; c++)
17041
if (int overlap_mask = CalcFontGlyphSrcOverlapMask(atlas, font, c))
17042
{
17043
unsigned int c_end = c + 1;
17044
while (c_end < 0x10000 && CalcFontGlyphSrcOverlapMask(atlas, font, c_end) == overlap_mask)
17045
c_end++;
17046
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)))
17047
{
17048
char utf8_buf[5];
17049
for (unsigned int n = c; n < c_end; n++)
17050
BulletText("Codepoint U+%04X (%s)", n, ImTextCharToUtf8(utf8_buf, n));
17051
TreePop();
17052
}
17053
TableNextColumn();
17054
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17055
if (overlap_mask & (1 << src_n))
17056
{
17057
Text("%d ", src_n);
17058
SameLine();
17059
}
17060
c = c_end - 1;
17061
}
17062
EndTable();
17063
}
17064
TreePop();
17065
}
17066
17067
// Display all glyphs of the fonts in separate pages of 256 characters
17068
for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++)
17069
{
17070
ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n];
17071
if (baked->ContainerFont != font)
17072
continue;
17073
PushID(baked_n);
17074
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*" : ""))
17075
{
17076
if (SmallButton("Load all"))
17077
for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)
17078
baked->FindGlyph((ImWchar)base);
17079
17080
const int surface_sqrt = (int)ImSqrt((float)baked->MetricsTotalSurface);
17081
Text("Ascent: %f, Descent: %f, Ascent-Descent: %f", baked->Ascent, baked->Descent, baked->Ascent - baked->Descent);
17082
Text("Texture Area: about %d px ~%dx%d px", baked->MetricsTotalSurface, surface_sqrt, surface_sqrt);
17083
for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17084
{
17085
ImFontConfig* src = font->Sources[src_n];
17086
int oversample_h, oversample_v;
17087
ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);
17088
BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
17089
src_n, src->Name, src->OversampleH, oversample_h, src->OversampleV, oversample_v, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y);
17090
}
17091
17092
DebugNodeFontGlyphesForSrcMask(font, baked, ~0);
17093
TreePop();
17094
}
17095
PopID();
17096
}
17097
TreePop();
17098
Unindent();
17099
}
17100
17101
void ImGui::DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask)
17102
{
17103
ImDrawList* draw_list = GetWindowDrawList();
17104
const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
17105
const float cell_size = baked->Size * 1;
17106
const float cell_spacing = GetStyle().ItemSpacing.y;
17107
for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
17108
{
17109
// Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
17110
// This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
17111
// is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
17112
if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191))
17113
{
17114
base += 8192 - 256;
17115
continue;
17116
}
17117
17118
int count = 0;
17119
for (unsigned int n = 0; n < 256; n++)
17120
if (const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL)
17121
if (src_mask & (1 << glyph->SourceIdx))
17122
count++;
17123
if (count <= 0)
17124
continue;
17125
if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
17126
continue;
17127
17128
// Draw a 16x16 grid of glyphs
17129
ImVec2 base_pos = GetCursorScreenPos();
17130
for (unsigned int n = 0; n < 256; n++)
17131
{
17132
// We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
17133
// available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
17134
ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
17135
ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
17136
const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL;
17137
draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
17138
if (!glyph || (src_mask & (1 << glyph->SourceIdx)) == 0)
17139
continue;
17140
font->RenderChar(draw_list, cell_size, 0.0f, cell_p1, glyph_col, (ImWchar)(base + n));
17141
if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
17142
{
17143
DebugNodeFontGlyph(font, glyph);
17144
EndTooltip();
17145
}
17146
}
17147
Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
17148
TreePop();
17149
}
17150
}
17151
17152
void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph)
17153
{
17154
Text("Codepoint: U+%04X", glyph->Codepoint);
17155
Separator();
17156
Text("Visible: %d", glyph->Visible);
17157
Text("AdvanceX: %.1f", glyph->AdvanceX);
17158
Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
17159
Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
17160
if (glyph->PackId >= 0)
17161
{
17162
ImTextureRect* r = ImFontAtlasPackGetRect(font->ContainerAtlas, glyph->PackId);
17163
Text("PackId: %d (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y);
17164
}
17165
Text("SourceIdx: %d", glyph->SourceIdx);
17166
}
17167
17168
// [DEBUG] Display contents of ImGuiStorage
17169
void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
17170
{
17171
if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
17172
return;
17173
for (const ImGuiStoragePair& p : storage->Data)
17174
{
17175
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.
17176
DebugLocateItemOnHover(p.key);
17177
}
17178
TreePop();
17179
}
17180
17181
// [DEBUG] Display contents of ImGuiTabBar
17182
void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
17183
{
17184
// Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
17185
char buf[256];
17186
char* p = buf;
17187
const char* buf_end = buf + IM_ARRAYSIZE(buf);
17188
const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
17189
p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
17190
for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
17191
{
17192
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17193
p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
17194
}
17195
p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
17196
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17197
bool open = TreeNode(label, "%s", buf);
17198
if (!is_active) { PopStyleColor(); }
17199
if (is_active && IsItemHovered())
17200
{
17201
ImDrawList* draw_list = GetForegroundDrawList();
17202
draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
17203
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));
17204
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));
17205
}
17206
if (open)
17207
{
17208
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
17209
{
17210
ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17211
PushID(tab);
17212
if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
17213
if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
17214
Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
17215
tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
17216
PopID();
17217
}
17218
TreePop();
17219
}
17220
}
17221
17222
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
17223
{
17224
ImGuiContext& g = *GImGui;
17225
SetNextItemOpen(true, ImGuiCond_Once);
17226
bool open = TreeNode("viewport0", "Viewport #%d", 0);
17227
if (IsItemHovered())
17228
g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
17229
if (open)
17230
{
17231
ImGuiWindowFlags flags = viewport->Flags;
17232
BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
17233
viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
17234
viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y);
17235
BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
17236
(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
17237
(flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
17238
(flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
17239
for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
17240
DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
17241
TreePop();
17242
}
17243
}
17244
17245
void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
17246
{
17247
if (window == NULL)
17248
{
17249
BulletText("%s: NULL", label);
17250
return;
17251
}
17252
17253
ImGuiContext& g = *GImGui;
17254
const bool is_active = window->WasActive;
17255
ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
17256
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17257
const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
17258
if (!is_active) { PopStyleColor(); }
17259
if (IsItemHovered() && is_active)
17260
GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
17261
if (!open)
17262
return;
17263
17264
if (window->MemoryCompacted)
17265
TextDisabled("Note: some memory buffers have been compacted/freed.");
17266
17267
if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
17268
g.DebugBreakInWindow = window->ID;
17269
17270
ImGuiWindowFlags flags = window->Flags;
17271
DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
17272
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);
17273
BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
17274
(flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
17275
(flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
17276
(flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
17277
if (flags & ImGuiWindowFlags_ChildWindow)
17278
BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags,
17279
(window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "",
17280
(window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "",
17281
(window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "",
17282
(window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : "");
17283
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" : "");
17284
BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
17285
BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
17286
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17287
{
17288
ImRect r = window->NavRectRel[layer];
17289
if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y)
17290
BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
17291
else
17292
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);
17293
DebugLocateItemOnHover(window->NavLastIds[layer]);
17294
}
17295
const ImVec2* pr = window->NavPreferredScoringPosRel;
17296
for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17297
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.
17298
BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
17299
if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
17300
if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
17301
if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
17302
if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
17303
if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
17304
{
17305
for (ImGuiOldColumns& columns : window->ColumnsStorage)
17306
DebugNodeColumns(&columns);
17307
TreePop();
17308
}
17309
DebugNodeStorage(&window->StateStorage, "Storage");
17310
TreePop();
17311
}
17312
17313
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
17314
{
17315
if (settings->WantDelete)
17316
BeginDisabled();
17317
Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
17318
settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
17319
if (settings->WantDelete)
17320
EndDisabled();
17321
}
17322
17323
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
17324
{
17325
if (!TreeNode(label, "%s (%d)", label, windows->Size))
17326
return;
17327
for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
17328
{
17329
PushID((*windows)[i]);
17330
DebugNodeWindow((*windows)[i], "Window");
17331
PopID();
17332
}
17333
TreePop();
17334
}
17335
17336
// FIXME-OPT: This is technically suboptimal, but it is simpler this way.
17337
void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
17338
{
17339
for (int i = 0; i < windows_size; i++)
17340
{
17341
ImGuiWindow* window = windows[i];
17342
if (window->ParentWindowInBeginStack != parent_in_begin_stack)
17343
continue;
17344
char buf[20];
17345
ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext);
17346
//BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
17347
DebugNodeWindow(window, buf);
17348
TreePush(buf);
17349
DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
17350
TreePop();
17351
}
17352
}
17353
17354
//-----------------------------------------------------------------------------
17355
// [SECTION] DEBUG LOG WINDOW
17356
//-----------------------------------------------------------------------------
17357
17358
void ImGui::DebugLog(const char* fmt, ...)
17359
{
17360
va_list args;
17361
va_start(args, fmt);
17362
DebugLogV(fmt, args);
17363
va_end(args);
17364
}
17365
17366
void ImGui::DebugLogV(const char* fmt, va_list args)
17367
{
17368
ImGuiContext& g = *GImGui;
17369
const int old_size = g.DebugLogBuf.size();
17370
if (g.ContextName[0] != 0)
17371
g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount);
17372
else
17373
g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
17374
g.DebugLogBuf.appendfv(fmt, args);
17375
g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
17376
if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
17377
IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
17378
#ifdef IMGUI_ENABLE_TEST_ENGINE
17379
// IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically
17380
const int new_size = g.DebugLogBuf.size();
17381
const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n');
17382
if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
17383
IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size);
17384
#endif
17385
}
17386
17387
// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
17388
static void SameLineOrWrap(const ImVec2& size)
17389
{
17390
ImGuiContext& g = *GImGui;
17391
ImGuiWindow* window = g.CurrentWindow;
17392
ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
17393
if (window->WorkRect.Contains(ImRect(pos, pos + size)))
17394
ImGui::SameLine();
17395
}
17396
17397
static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
17398
{
17399
ImGuiContext& g = *GImGui;
17400
ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
17401
SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
17402
17403
bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
17404
if (highlight_errors)
17405
ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
17406
if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
17407
{
17408
g.DebugLogAutoDisableFrames = 2;
17409
g.DebugLogAutoDisableFlags |= flags;
17410
}
17411
if (highlight_errors)
17412
{
17413
ImGui::PopStyleColor();
17414
ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
17415
}
17416
else
17417
{
17418
ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
17419
}
17420
}
17421
17422
void ImGui::ShowDebugLogWindow(bool* p_open)
17423
{
17424
ImGuiContext& g = *GImGui;
17425
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)
17426
SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
17427
if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
17428
{
17429
End();
17430
return;
17431
}
17432
17433
ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
17434
CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
17435
SetItemTooltip("(except InputRouting which is spammy)");
17436
17437
ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
17438
ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
17439
ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
17440
ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
17441
ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
17442
ShowDebugLogFlag("Font", ImGuiDebugLogFlags_EventFont);
17443
ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
17444
ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
17445
ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
17446
ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
17447
17448
if (SmallButton("Clear"))
17449
{
17450
g.DebugLogBuf.clear();
17451
g.DebugLogIndex.clear();
17452
g.DebugLogSkippedErrors = 0;
17453
}
17454
SameLine();
17455
if (SmallButton("Copy"))
17456
SetClipboardText(g.DebugLogBuf.c_str());
17457
SameLine();
17458
if (SmallButton("Configure Outputs.."))
17459
OpenPopup("Outputs");
17460
if (BeginPopup("Outputs"))
17461
{
17462
CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);
17463
#ifndef IMGUI_ENABLE_TEST_ENGINE
17464
BeginDisabled();
17465
#endif
17466
CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);
17467
#ifndef IMGUI_ENABLE_TEST_ENGINE
17468
EndDisabled();
17469
#endif
17470
EndPopup();
17471
}
17472
17473
BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
17474
17475
const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
17476
g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
17477
17478
ImGuiListClipper clipper;
17479
clipper.Begin(g.DebugLogIndex.size());
17480
while (clipper.Step())
17481
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
17482
DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));
17483
g.DebugLogFlags = backup_log_flags;
17484
if (GetScrollY() >= GetScrollMaxY())
17485
SetScrollHereY(1.0f);
17486
EndChild();
17487
17488
End();
17489
}
17490
17491
// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
17492
void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)
17493
{
17494
TextUnformatted(line_begin, line_end);
17495
if (!IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
17496
return;
17497
ImGuiContext& g = *GImGui;
17498
ImRect text_rect = g.LastItemData.Rect;
17499
for (const char* p = line_begin; p <= line_end - 10; p++)
17500
{
17501
ImGuiID id = 0;
17502
if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10]))
17503
continue;
17504
ImVec2 p0 = CalcTextSize(line_begin, p);
17505
ImVec2 p1 = CalcTextSize(p, p + 10);
17506
g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
17507
if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
17508
DebugLocateItemOnHover(id);
17509
p += 10;
17510
}
17511
}
17512
17513
//-----------------------------------------------------------------------------
17514
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
17515
//-----------------------------------------------------------------------------
17516
17517
// Draw a small cross at current CursorPos in current window's DrawList
17518
void ImGui::DebugDrawCursorPos(ImU32 col)
17519
{
17520
ImGuiContext& g = *GImGui;
17521
ImGuiWindow* window = g.CurrentWindow;
17522
ImVec2 pos = window->DC.CursorPos;
17523
window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
17524
window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
17525
}
17526
17527
// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
17528
void ImGui::DebugDrawLineExtents(ImU32 col)
17529
{
17530
ImGuiContext& g = *GImGui;
17531
ImGuiWindow* window = g.CurrentWindow;
17532
float curr_x = window->DC.CursorPos.x;
17533
float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
17534
float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
17535
window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
17536
window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
17537
window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
17538
}
17539
17540
// Draw last item rect in ForegroundDrawList (so it is always visible)
17541
void ImGui::DebugDrawItemRect(ImU32 col)
17542
{
17543
ImGuiContext& g = *GImGui;
17544
ImGuiWindow* window = g.CurrentWindow;
17545
GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
17546
}
17547
17548
// [DEBUG] Locate item position/rectangle given an ID.
17549
static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green
17550
17551
void ImGui::DebugLocateItem(ImGuiID target_id)
17552
{
17553
ImGuiContext& g = *GImGui;
17554
g.DebugLocateId = target_id;
17555
g.DebugLocateFrames = 2;
17556
g.DebugBreakInLocateId = false;
17557
}
17558
17559
// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
17560
void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
17561
{
17562
if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
17563
return;
17564
ImGuiContext& g = *GImGui;
17565
DebugLocateItem(target_id);
17566
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);
17567
17568
// Can't easily use a context menu here because it will mess with focus, active id etc.
17569
if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
17570
{
17571
DebugBreakButtonTooltip(false, "in ItemAdd()");
17572
if (IsKeyChordPressed(g.DebugBreakKeyChord))
17573
g.DebugBreakInLocateId = true;
17574
}
17575
}
17576
17577
void ImGui::DebugLocateItemResolveWithLastItem()
17578
{
17579
ImGuiContext& g = *GImGui;
17580
17581
// [DEBUG] Debug break requested by user
17582
if (g.DebugBreakInLocateId)
17583
IM_DEBUG_BREAK();
17584
17585
ImGuiLastItemData item_data = g.LastItemData;
17586
g.DebugLocateId = 0;
17587
ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
17588
ImRect r = item_data.Rect;
17589
r.Expand(3.0f);
17590
ImVec2 p1 = g.IO.MousePos;
17591
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);
17592
draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
17593
draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
17594
}
17595
17596
void ImGui::DebugStartItemPicker()
17597
{
17598
ImGuiContext& g = *GImGui;
17599
g.DebugItemPickerActive = true;
17600
}
17601
17602
// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
17603
void ImGui::UpdateDebugToolItemPicker()
17604
{
17605
ImGuiContext& g = *GImGui;
17606
g.DebugItemPickerBreakId = 0;
17607
if (!g.DebugItemPickerActive)
17608
return;
17609
17610
const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
17611
SetMouseCursor(ImGuiMouseCursor_Hand);
17612
if (IsKeyPressed(ImGuiKey_Escape))
17613
g.DebugItemPickerActive = false;
17614
const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
17615
if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
17616
{
17617
g.DebugItemPickerBreakId = hovered_id;
17618
g.DebugItemPickerActive = false;
17619
}
17620
for (int mouse_button = 0; mouse_button < 3; mouse_button++)
17621
if (change_mapping && IsMouseClicked(mouse_button))
17622
g.DebugItemPickerMouseButton = (ImU8)mouse_button;
17623
SetNextWindowBgAlpha(0.70f);
17624
if (!BeginTooltip())
17625
return;
17626
Text("HoveredId: 0x%08X", hovered_id);
17627
Text("Press ESC to abort picking.");
17628
const char* mouse_button_names[] = { "Left", "Right", "Middle" };
17629
if (change_mapping)
17630
Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
17631
else
17632
TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
17633
EndTooltip();
17634
}
17635
17636
// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
17637
void ImGui::UpdateDebugToolStackQueries()
17638
{
17639
ImGuiContext& g = *GImGui;
17640
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
17641
17642
// Clear hook when id stack tool is not visible
17643
g.DebugHookIdInfo = 0;
17644
if (g.FrameCount != tool->LastActiveFrame + 1)
17645
return;
17646
17647
// Update queries. The steps are: -1: query Stack, >= 0: query each stack item
17648
// We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
17649
const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
17650
if (tool->QueryId != query_id)
17651
{
17652
tool->QueryId = query_id;
17653
tool->StackLevel = -1;
17654
tool->Results.resize(0);
17655
}
17656
if (query_id == 0)
17657
return;
17658
17659
// Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
17660
int stack_level = tool->StackLevel;
17661
if (stack_level >= 0 && stack_level < tool->Results.Size)
17662
if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
17663
tool->StackLevel++;
17664
17665
// Update hook
17666
stack_level = tool->StackLevel;
17667
if (stack_level == -1)
17668
g.DebugHookIdInfo = query_id;
17669
if (stack_level >= 0 && stack_level < tool->Results.Size)
17670
{
17671
g.DebugHookIdInfo = tool->Results[stack_level].ID;
17672
tool->Results[stack_level].QueryFrameCount++;
17673
}
17674
}
17675
17676
// [DEBUG] ID Stack tool: hooks called by GetID() family functions
17677
void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
17678
{
17679
ImGuiContext& g = *GImGui;
17680
ImGuiWindow* window = g.CurrentWindow;
17681
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
17682
17683
// Step 0: stack query
17684
// This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
17685
if (tool->StackLevel == -1)
17686
{
17687
tool->StackLevel++;
17688
tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
17689
for (int n = 0; n < window->IDStack.Size + 1; n++)
17690
tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
17691
return;
17692
}
17693
17694
// Step 1+: query for individual level
17695
IM_ASSERT(tool->StackLevel >= 0);
17696
if (tool->StackLevel != window->IDStack.Size)
17697
return;
17698
ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
17699
IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
17700
17701
switch (data_type)
17702
{
17703
case ImGuiDataType_S32:
17704
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id);
17705
break;
17706
case ImGuiDataType_String:
17707
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id);
17708
break;
17709
case ImGuiDataType_Pointer:
17710
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id);
17711
break;
17712
case ImGuiDataType_ID:
17713
if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
17714
return;
17715
ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id);
17716
break;
17717
default:
17718
IM_ASSERT(0);
17719
}
17720
info->QuerySuccess = true;
17721
info->DataType = data_type;
17722
}
17723
17724
static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
17725
{
17726
ImGuiStackLevelInfo* info = &tool->Results[n];
17727
ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
17728
if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
17729
return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", window->Name);
17730
if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
17731
return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", info->Desc);
17732
if (tool->StackLevel < tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
17733
return (*buf = 0);
17734
#ifdef IMGUI_ENABLE_TEST_ENGINE
17735
if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID)) // Source: ImGuiTestEngine's ItemInfo()
17736
return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", label);
17737
#endif
17738
return ImFormatString(buf, buf_size, "???");
17739
}
17740
17741
// ID Stack Tool: Display UI
17742
void ImGui::ShowIDStackToolWindow(bool* p_open)
17743
{
17744
ImGuiContext& g = *GImGui;
17745
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)
17746
SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
17747
if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
17748
{
17749
End();
17750
return;
17751
}
17752
17753
// Display hovered/active status
17754
ImGuiIDStackTool* tool = &g.DebugIDStackTool;
17755
17756
// Build and display path
17757
tool->ResultPathBuf.resize(0);
17758
for (int stack_n = 0; stack_n < tool->Results.Size; stack_n++)
17759
{
17760
char level_desc[256];
17761
StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
17762
tool->ResultPathBuf.append(stack_n == 0 ? "//" : "/");
17763
for (int n = 0; level_desc[n]; n++)
17764
{
17765
if (level_desc[n] == '/')
17766
tool->ResultPathBuf.append("\\");
17767
tool->ResultPathBuf.append(level_desc + n, level_desc + n + 1);
17768
}
17769
}
17770
Text("0x%08X", tool->QueryId);
17771
SameLine();
17772
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.");
17773
17774
// CTRL+C to copy path
17775
const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
17776
SameLine();
17777
PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f); Checkbox("Ctrl+C: copy path", &tool->CopyToClipboardOnCtrlC); PopStyleVar();
17778
SameLine();
17779
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*");
17780
if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
17781
{
17782
tool->CopyToClipboardLastTime = (float)g.Time;
17783
SetClipboardText(tool->ResultPathBuf.c_str());
17784
}
17785
17786
Text("- Path \"%s\"", tool->ResultPathBuf.c_str());
17787
#ifdef IMGUI_ENABLE_TEST_ENGINE
17788
Text("- Label \"%s\"", tool->QueryId ? ImGuiTestEngine_FindItemDebugLabel(&g, tool->QueryId) : "");
17789
#endif
17790
17791
Separator();
17792
17793
// Display decorated stack
17794
tool->LastActiveFrame = g.FrameCount;
17795
if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
17796
{
17797
const float id_width = CalcTextSize("0xDDDDDDDD").x;
17798
TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
17799
TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
17800
TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
17801
TableHeadersRow();
17802
for (int n = 0; n < tool->Results.Size; n++)
17803
{
17804
ImGuiStackLevelInfo* info = &tool->Results[n];
17805
TableNextColumn();
17806
Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
17807
TableNextColumn();
17808
StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
17809
TextUnformatted(g.TempBuffer.Data);
17810
TableNextColumn();
17811
Text("0x%08X", info->ID);
17812
if (n == tool->Results.Size - 1)
17813
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
17814
}
17815
EndTable();
17816
}
17817
End();
17818
}
17819
17820
#else
17821
17822
void ImGui::ShowMetricsWindow(bool*) {}
17823
void ImGui::ShowFontAtlas(ImFontAtlas*) {}
17824
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
17825
void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
17826
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
17827
void ImGui::DebugNodeFont(ImFont*) {}
17828
void ImGui::DebugNodeFontGlyphesForSrcMask(ImFont*, ImFontBaked*, int) {}
17829
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
17830
void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
17831
void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
17832
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
17833
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
17834
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
17835
17836
void ImGui::ShowDebugLogWindow(bool*) {}
17837
void ImGui::ShowIDStackToolWindow(bool*) {}
17838
void ImGui::DebugStartItemPicker() {}
17839
void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
17840
17841
#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
17842
17843
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
17844
// Demo helper function to select among loaded fonts.
17845
// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
17846
void ImGui::ShowFontSelector(const char* label)
17847
{
17848
ImGuiIO& io = GetIO();
17849
ImFont* font_current = GetFont();
17850
if (BeginCombo(label, font_current->GetDebugName()))
17851
{
17852
for (ImFont* font : io.Fonts->Fonts)
17853
{
17854
PushID((void*)font);
17855
if (Selectable(font->GetDebugName(), font == font_current))
17856
io.FontDefault = font;
17857
if (font == font_current)
17858
SetItemDefaultFocus();
17859
PopID();
17860
}
17861
EndCombo();
17862
}
17863
SameLine();
17864
if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
17865
MetricsHelpMarker(
17866
"- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
17867
"- Read FAQ and docs/FONTS.md for more details.");
17868
else
17869
MetricsHelpMarker(
17870
"- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
17871
"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
17872
"- Read FAQ and docs/FONTS.md for more details.\n"
17873
"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
17874
}
17875
#endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
17876
17877
//-----------------------------------------------------------------------------
17878
17879
// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
17880
// 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.
17881
#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
17882
#include "imgui_user.inl"
17883
#endif
17884
17885
//-----------------------------------------------------------------------------
17886
17887
#endif // #ifndef IMGUI_DISABLE
17888
17889