Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/imgui/imgui.cpp
4558 views
1
// dear imgui, v1.68 WIP
2
// (main code and documentation)
3
4
// Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5
// Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6
// Get latest version at https://github.com/ocornut/imgui
7
// Releases change-log at https://github.com/ocornut/imgui/releases
8
// Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started
9
// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
10
11
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
12
// See LICENSE.txt for copyright and licensing details (standard MIT License).
13
// This library is free but I need your support to sustain development and maintenance.
14
// Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README.
15
// Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui.
16
17
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
18
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
19
// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
20
// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
21
// to a better solution or official support for them.
22
23
/*
24
25
Index of this file:
26
27
DOCUMENTATION
28
29
- MISSION STATEMENT
30
- END-USER GUIDE
31
- PROGRAMMER GUIDE (read me!)
32
- Read first.
33
- How to update to a newer version of Dear ImGui.
34
- Getting started with integrating Dear ImGui in your code/engine.
35
- This is how a simple application may look like (2 variations).
36
- This is how a simple rendering function may look like.
37
- Using gamepad/keyboard navigation controls.
38
- API BREAKING CHANGES (read me when you update!)
39
- FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
40
- How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
41
- How can I display an image? What is ImTextureID, how does it works?
42
- How can I have multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack.
43
- How can I use my own math types instead of ImVec2/ImVec4?
44
- How can I load a different font than the default?
45
- How can I easily use icons in my application?
46
- How can I load multiple fonts?
47
- How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
48
- How can I interact with standard C++ types (such as std::string and std::vector)?
49
- How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
50
- How can I use Dear ImGui on a platform that doesn't have a mouse or a keyboard? (input share, remoting, gamepad)
51
- I integrated Dear ImGui in my engine and the text or lines are blurry..
52
- I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
53
- How can I help?
54
55
CODE
56
(search for "[SECTION]" in the code to find them)
57
58
// [SECTION] FORWARD DECLARATIONS
59
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
60
// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
61
// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
62
// [SECTION] MISC HELPER/UTILITIES (ImText* functions)
63
// [SECTION] MISC HELPER/UTILITIES (Color functions)
64
// [SECTION] ImGuiStorage
65
// [SECTION] ImGuiTextFilter
66
// [SECTION] ImGuiTextBuffer
67
// [SECTION] ImGuiListClipper
68
// [SECTION] RENDER HELPERS
69
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
70
// [SECTION] TOOLTIPS
71
// [SECTION] POPUPS
72
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
73
// [SECTION] COLUMNS
74
// [SECTION] DRAG AND DROP
75
// [SECTION] LOGGING/CAPTURING
76
// [SECTION] SETTINGS
77
// [SECTION] PLATFORM DEPENDENT HELPERS
78
// [SECTION] METRICS/DEBUG WINDOW
79
80
*/
81
82
//-----------------------------------------------------------------------------
83
// DOCUMENTATION
84
//-----------------------------------------------------------------------------
85
86
/*
87
88
MISSION STATEMENT
89
=================
90
91
- Easy to use to create code-driven and data-driven tools.
92
- Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
93
- Easy to hack and improve.
94
- Minimize screen real-estate usage.
95
- Minimize setup and maintenance.
96
- Minimize state storage on user side.
97
- Portable, minimize dependencies, run on target (consoles, phones, etc.).
98
- Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
99
opening a tree node for the first time, etc. but a typical frame should not allocate anything).
100
101
Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
102
- Doesn't look fancy, doesn't animate.
103
- Limited layout features, intricate layouts are typically crafted in code.
104
105
106
END-USER GUIDE
107
==============
108
109
- Double-click on title bar to collapse window.
110
- Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
111
- Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
112
- Click and drag on any empty space to move window.
113
- TAB/SHIFT+TAB to cycle through keyboard editable fields.
114
- CTRL+Click on a slider or drag box to input value as text.
115
- Use mouse wheel to scroll.
116
- Text editor:
117
- Hold SHIFT or use mouse to select text.
118
- CTRL+Left/Right to word jump.
119
- CTRL+Shift+Left/Right to select words.
120
- CTRL+A our Double-Click to select all.
121
- CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
122
- CTRL+Z,CTRL+Y to undo/redo.
123
- ESCAPE to revert text to its original value.
124
- You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
125
- Controls are automatically adjusted for OSX to match standard OSX text editing operations.
126
- General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
127
- General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
128
129
130
PROGRAMMER GUIDE
131
================
132
133
READ FIRST:
134
135
- Read the FAQ below this section!
136
- Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction
137
or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
138
- Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
139
- The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
140
- Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
141
You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
142
- Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
143
For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI,
144
where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
145
- Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
146
- This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
147
- Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
148
If you get an assert, read the messages and comments around the assert.
149
- C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace.
150
- C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
151
See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
152
However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
153
- C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
154
155
HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI:
156
157
- Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
158
- Or maintain your own branch where you have imconfig.h modified.
159
- Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
160
If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
161
from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
162
likely be a comment about it. Please report any issue to the GitHub page!
163
- Try to keep your copy of dear imgui reasonably up to date.
164
165
GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE:
166
167
- Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
168
- Add the Dear ImGui source files to your projects or using your preferred build system.
169
It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
170
- You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types.
171
- When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
172
- Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
173
Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
174
phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
175
- Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
176
- 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.
177
178
HOW A SIMPLE APPLICATION MAY LOOK LIKE:
179
EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
180
181
// Application init: create a dear imgui context, setup some options, load fonts
182
ImGui::CreateContext();
183
ImGuiIO& io = ImGui::GetIO();
184
// TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
185
// TODO: Fill optional fields of the io structure later.
186
// TODO: Load TTF/OTF fonts if you don't want to use the default font.
187
188
// Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
189
ImGui_ImplWin32_Init(hwnd);
190
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
191
192
// Application main loop
193
while (true)
194
{
195
// Feed inputs to dear imgui, start new frame
196
ImGui_ImplDX11_NewFrame();
197
ImGui_ImplWin32_NewFrame();
198
ImGui::NewFrame();
199
200
// Any application code here
201
ImGui::Text("Hello, world!");
202
203
// Render dear imgui into screen
204
ImGui::Render();
205
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
206
g_pSwapChain->Present(1, 0);
207
}
208
209
// Shutdown
210
ImGui_ImplDX11_Shutdown();
211
ImGui_ImplWin32_Shutdown();
212
ImGui::DestroyContext();
213
214
HOW A SIMPLE APPLICATION MAY LOOK LIKE:
215
EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE.
216
217
// Application init: create a dear imgui context, setup some options, load fonts
218
ImGui::CreateContext();
219
ImGuiIO& io = ImGui::GetIO();
220
// TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
221
// TODO: Fill optional fields of the io structure later.
222
// TODO: Load TTF/OTF fonts if you don't want to use the default font.
223
224
// Build and load the texture atlas into a texture
225
// (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
226
int width, height;
227
unsigned char* pixels = NULL;
228
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
229
230
// At this point you've got the texture data and you need to upload that your your graphic system:
231
// After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
232
// This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
233
MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
234
io.Fonts->TexID = (void*)texture;
235
236
// Application main loop
237
while (true)
238
{
239
// Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
240
// (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
241
io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
242
io.DisplaySize.x = 1920.0f; // set the current display width
243
io.DisplaySize.y = 1280.0f; // set the current display height here
244
io.MousePos = my_mouse_pos; // set the mouse position
245
io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
246
io.MouseDown[1] = my_mouse_buttons[1];
247
248
// Call NewFrame(), after this point you can use ImGui::* functions anytime
249
// (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
250
ImGui::NewFrame();
251
252
// Most of your application code here
253
ImGui::Text("Hello, world!");
254
MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
255
MyGameRender(); // may use any ImGui functions as well!
256
257
// Render imgui, swap buffers
258
// (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
259
ImGui::EndFrame();
260
ImGui::Render();
261
ImDrawData* draw_data = ImGui::GetDrawData();
262
MyImGuiRenderFunction(draw_data);
263
SwapBuffers();
264
}
265
266
// Shutdown
267
ImGui::DestroyContext();
268
269
HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE:
270
271
void void MyImGuiRenderFunction(ImDrawData* draw_data)
272
{
273
// TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
274
// TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
275
// TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
276
// TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
277
for (int n = 0; n < draw_data->CmdListsCount; n++)
278
{
279
const ImDrawList* cmd_list = draw_data->CmdLists[n];
280
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui
281
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui
282
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
283
{
284
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
285
if (pcmd->UserCallback)
286
{
287
pcmd->UserCallback(cmd_list, pcmd);
288
}
289
else
290
{
291
// The texture for the draw call is specified by pcmd->TextureId.
292
// The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
293
MyEngineBindTexture((MyTexture*)pcmd->TextureId);
294
295
// We are using scissoring to clip some objects. All low-level graphics API should supports it.
296
// - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
297
// (some elements visible outside their bounds) but you can fix that once everything else works!
298
// - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
299
// In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
300
// However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
301
// always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
302
// - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
303
ImVec2 pos = draw_data->DisplayPos;
304
MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y));
305
306
// Render 'pcmd->ElemCount/3' indexed triangles.
307
// By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
308
MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
309
}
310
idx_buffer += pcmd->ElemCount;
311
}
312
}
313
}
314
315
- The examples/ folders contains many actual implementation of the pseudo-codes above.
316
- When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
317
They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs
318
from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information.
319
- Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues!
320
321
USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
322
323
- The gamepad/keyboard navigation is fairly functional and keeps being improved.
324
- Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
325
- You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
326
- The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
327
- Gamepad:
328
- Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
329
- Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
330
Note that io.NavInputs[] is cleared by EndFrame().
331
- See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
332
0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
333
- We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
334
Your 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.).
335
- You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
336
- If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo
337
to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
338
- Keyboard:
339
- Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
340
NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
341
- When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
342
will be set. For more advanced uses, you may want to read from:
343
- io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
344
- io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
345
- or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
346
Please reach out if you think the game vs navigation input sharing could be improved.
347
- Mouse:
348
- PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
349
- Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard.
350
- On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.
351
Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
352
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.
353
When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
354
(If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
355
(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
356
to set a boolean to ignore your other external mouse positions until the external source is moved again.)
357
358
359
API BREAKING CHANGES
360
====================
361
362
Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
363
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.
364
When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
365
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
366
367
- 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 a dummy small value!
368
- 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
369
- 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
370
- 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete).
371
- 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.
372
- 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
373
- 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
374
- 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
375
- 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.
376
If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
377
- 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
378
- 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.
379
NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
380
Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
381
- 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).
382
- 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
383
- 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
384
- 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.
385
- 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
386
- 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
387
- 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
388
- 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.).
389
old binding will still work as is, however prefer using the separated bindings as they will be updated to be multi-viewport conformant.
390
when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
391
- 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
392
- 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
393
- 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.
394
If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
395
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.
396
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.
397
- 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",
398
consistent with other functions. Kept redirection functions (will obsolete).
399
- 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.
400
- 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 binding ahead of merging the Nav branch).
401
- 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
402
- 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.
403
- 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
404
- 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.
405
- 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.
406
- 2018/02/07 (1.60) - reorganized context handling to be more explicit,
407
- YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
408
- removed Shutdown() function, as DestroyContext() serve this purpose.
409
- you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
410
- removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
411
- removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
412
- 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.
413
- 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
414
- 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
415
- 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
416
- 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.
417
- 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
418
- 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
419
- 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.
420
- 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.
421
- 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
422
- 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
423
- obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
424
- 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
425
- 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
426
- 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.
427
- 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.
428
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.
429
- 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
430
- 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
431
- 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
432
- 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
433
- 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.
434
- 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.
435
- 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.
436
removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
437
- 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
438
- 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
439
- 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Keep redirection typedef (will obsolete).
440
- 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).
441
- 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 binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
442
- 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
443
- renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
444
- renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
445
- 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
446
- 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.
447
- 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
448
- 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.
449
- 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
450
- 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
451
- 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
452
- 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.
453
- 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.
454
- 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))'
455
- 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
456
- 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
457
- 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
458
- 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().
459
- 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
460
- 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.
461
- 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.
462
- 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
463
If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
464
If your TitleBg/TitleBgActive alpha was <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.
465
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.
466
ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
467
{
468
float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
469
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);
470
}
471
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.
472
- 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
473
- 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
474
- 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).
475
- 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.
476
- 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).
477
- 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)
478
- 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
479
- 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.
480
- 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.
481
- 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
482
- 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
483
- 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
484
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.
485
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!
486
- 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
487
- 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.
488
- 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
489
- 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.
490
you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
491
- 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.
492
this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
493
- if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
494
- the signature of the io.RenderDrawListsFn handler has changed!
495
old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
496
new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
497
parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
498
ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
499
ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
500
- 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.
501
- 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!
502
- refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
503
- 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
504
- 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
505
- 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.
506
- 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
507
- 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
508
- 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
509
- 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
510
- 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.
511
- 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.
512
- 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
513
- 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
514
- 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
515
- 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
516
- 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
517
- 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
518
- 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
519
- 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
520
- 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
521
- 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
522
- 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
523
- 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
524
- 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
525
- 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
526
- 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
527
- 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
528
- 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
529
(1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
530
font init: { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
531
became: { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
532
you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
533
it is now recommended that you sample the font texture with bilinear interpolation.
534
(1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
535
(1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
536
(1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
537
- 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
538
- 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
539
- 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
540
- 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
541
- 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)
542
- 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
543
- 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
544
- 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
545
- 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
546
- 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
547
- 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
548
549
550
FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
551
======================================
552
553
Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
554
A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
555
- When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
556
- When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
557
- When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
558
Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
559
This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
560
Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
561
It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
562
Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
563
perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
564
Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
565
have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
566
were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
567
568
Q: How can I display an image? What is ImTextureID, how does it works?
569
A: Short explanation:
570
- You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures.
571
- Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
572
- Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
573
Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
574
575
Long explanation:
576
- Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
577
At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code
578
to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.).
579
- Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
580
We carry the information to identify a "texture" in the ImTextureID type.
581
ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice.
582
Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
583
- In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
584
an image from the end-user perspective. This is what the _examples_ rendering functions are using:
585
586
OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
587
DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp)
588
DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp)
589
DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp)
590
591
For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
592
Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
593
tying together both the texture and information about its format and how to read it.
594
- If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about
595
the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase
596
is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
597
If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID
598
representation suggested by the example bindings is probably the best choice.
599
(Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
600
601
User code may do:
602
603
// Cast our texture type to ImTextureID / void*
604
MyTexture* texture = g_CoffeeTableTexture;
605
ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
606
607
The renderer function called after ImGui::Render() will receive that same value that the user code passed:
608
609
// Cast ImTextureID / void* stored in the draw command as our texture type
610
MyTexture* texture = (MyTexture*)pcmd->TextureId;
611
MyEngineBindTexture2D(texture);
612
613
Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
614
This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them.
615
If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using.
616
617
Here's a simplified OpenGL example using stb_image.h:
618
619
// Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
620
#define STB_IMAGE_IMPLEMENTATION
621
#include <stb_image.h>
622
[...]
623
int my_image_width, my_image_height;
624
unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
625
626
// Turn the RGBA pixel data into an OpenGL texture:
627
GLuint my_opengl_texture;
628
glGenTextures(1, &my_opengl_texture);
629
glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
630
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
631
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
632
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
633
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
634
635
// Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it:
636
ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
637
638
C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa.
639
Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
640
Examples:
641
642
GLuint my_tex = XXX;
643
void* my_void_ptr;
644
my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
645
my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint
646
647
ID3D11ShaderResourceView* my_dx11_srv = XXX;
648
void* my_void_ptr;
649
my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void*
650
my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView*
651
652
Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
653
654
Q: How can I have multiple widgets with the same label or with an empty label?
655
Q: I have multiple widgets with the same label, and only the first one works. Why is that?
656
A: A primer on labels and the ID Stack...
657
658
Dear ImGui internally need to uniquely identify UI elements.
659
Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
660
Interactive widgets (such as calls to Button buttons) need a unique ID.
661
Unique ID are used internally to track active widgets and occasionally associate state to widgets.
662
Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
663
664
- Unique ID are often derived from a string label:
665
666
Button("OK"); // Label = "OK", ID = hash of (..., "OK")
667
Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
668
669
- ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
670
two buttons labeled "OK" in different windows or different tree locations is fine.
671
We used "..." above to signify whatever was already pushed to the ID stack previously:
672
673
Begin("MyWindow");
674
Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
675
End();
676
Begin("MyOtherWindow");
677
Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
678
End();
679
680
- If you have a same ID twice in the same location, you'll have a conflict:
681
682
Button("OK");
683
Button("OK"); // ID collision! Interacting with either button will trigger the first one.
684
685
Fear not! this is easy to solve and there are many ways to solve it!
686
687
- Solving ID conflict in a simple/local context:
688
When passing a label you can optionally specify extra ID information within string itself.
689
Use "##" to pass a complement to the ID that won't be visible to the end-user.
690
This helps solving the simple collision cases when you know e.g. at compilation time which items
691
are going to be created:
692
693
Begin("MyWindow");
694
Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
695
Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
696
Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
697
End();
698
699
- If you want to completely hide the label, but still need an ID:
700
701
Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
702
703
- Occasionally/rarely you might want change a label while preserving a constant ID. This allows
704
you to animate labels. For example you may want to include varying information in a window title bar,
705
but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
706
707
Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID")
708
Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different
709
710
sprintf(buf, "My game (%f FPS)###MyGame", fps);
711
Begin(buf); // Variable title, ID = hash of "MyGame"
712
713
- Solving ID conflict in a more general manner:
714
Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
715
within the same window. This is the most convenient way of distinguishing ID when iterating and
716
creating many UI elements programmatically.
717
You can push a pointer, a string or an integer value into the ID stack.
718
Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
719
At each level of the stack we store the seed used for items at this level of the ID stack.
720
721
Begin("Window");
722
for (int i = 0; i < 100; i++)
723
{
724
PushID(i); // Push i to the id tack
725
Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click")
726
PopID();
727
}
728
for (int i = 0; i < 100; i++)
729
{
730
MyObject* obj = Objects[i];
731
PushID(obj);
732
Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click")
733
PopID();
734
}
735
for (int i = 0; i < 100; i++)
736
{
737
MyObject* obj = Objects[i];
738
PushID(obj->Name);
739
Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click")
740
PopID();
741
}
742
End();
743
744
- You can stack multiple prefixes into the ID stack:
745
746
Button("Click"); // Label = "Click", ID = hash of (..., "Click")
747
PushID("node");
748
Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
749
PushID(my_ptr);
750
Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
751
PopID();
752
PopID();
753
754
- Tree nodes implicitly creates a scope for you by calling PushID().
755
756
Button("Click"); // Label = "Click", ID = hash of (..., "Click")
757
if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
758
{
759
Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
760
TreePop();
761
}
762
763
- When working with trees, ID are used to preserve the open/close state of each tree node.
764
Depending on your use cases you may want to use strings, indices or pointers as ID.
765
e.g. when following a single pointer that may change over time, using a static string as ID
766
will preserve your node open/closed state when the targeted object change.
767
e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
768
node open/closed state differently. See what makes more sense in your situation!
769
770
Q: How can I use my own math types instead of ImVec2/ImVec4?
771
A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
772
This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
773
774
Q: How can I load a different font than the default?
775
A: Use the font atlas to load the TTF/OTF file you want:
776
ImGuiIO& io = ImGui::GetIO();
777
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
778
io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
779
Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code.
780
(Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.)
781
(Read the 'misc/fonts/README.txt' file for more details about font loading.)
782
783
New programmers: remember that in C/C++ and most programming languages if you want to use a
784
backslash \ within a string literal, you need to write it double backslash "\\":
785
io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!)
786
io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT
787
io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT
788
789
Q: How can I easily use icons in my application?
790
A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
791
main font. Then you can refer to icons within your strings.
792
You may want to see ImFontConfig::GlyphMinAdvanceX to make your icon look monospace to facilitate alignment.
793
(Read the 'misc/fonts/README.txt' file for more details about icons font loading.)
794
795
Q: How can I load multiple fonts?
796
A: Use the font atlas to pack them into a single texture:
797
(Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.)
798
799
ImGuiIO& io = ImGui::GetIO();
800
ImFont* font0 = io.Fonts->AddFontDefault();
801
ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
802
ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
803
io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
804
// the first loaded font gets used by default
805
// use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
806
807
// Options
808
ImFontConfig config;
809
config.OversampleH = 2;
810
config.OversampleV = 1;
811
config.GlyphOffset.y -= 1.0f; // Move everything by 1 pixels up
812
config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
813
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config);
814
815
// Combine multiple fonts into one (e.g. for icon fonts)
816
static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
817
ImFontConfig config;
818
config.MergeMode = true;
819
io.Fonts->AddFontDefault();
820
io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
821
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
822
823
Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
824
A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
825
826
// Add default Japanese ranges
827
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
828
829
// Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
830
ImVector<ImWchar> ranges;
831
ImFontGlyphRangesBuilder builder;
832
builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
833
builder.AddChar(0x7262); // Add a specific character
834
builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
835
builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
836
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
837
838
All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
839
by using the u8"hello" syntax. Specifying literal in your source code using a local code page
840
(such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
841
Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
842
843
Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
844
The applications in examples/ are doing that.
845
Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
846
You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
847
Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for
848
the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
849
850
Q: How can I interact with standard C++ types (such as std::string and std::vector)?
851
A: - Being highly portable (bindings for several languages, frameworks, programming style, obscure or older platforms/compilers),
852
and aiming for compatibility & performance suitable for every modern real-time game engines, dear imgui does not use
853
any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases.
854
- To use ImGui::InputText() with a std::string or any resizable string class, see misc/cpp/imgui_stdlib.h.
855
- To use combo boxes and list boxes with std::vector or any other data structure: the BeginCombo()/EndCombo() API
856
lets you iterate and submit items yourself, so does the ListBoxHeader()/ListBoxFooter() API.
857
Prefer using them over the old and awkward Combo()/ListBox() api.
858
- Generally for most high-level types you should be able to access the underlying data type.
859
You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code).
860
- Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass
861
to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application.
862
Please bear in mind that using std::string on applications with large amount of UI may incur unsatisfactory performances.
863
Modern implementations of std::string often include small-string optimization (which is often a local buffer) but those
864
are not configurable and not the same across implementations.
865
- If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to excessive amount
866
of heap allocations. Consider using literals, statically sized buffers and your own helper functions. A common pattern
867
is that you will need to build lots of strings on the fly, and their maximum length can be easily be scoped ahead.
868
One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str
869
This is a small helper where you can instance strings with configurable local buffers length. Many game engines will
870
provide similar or better string helpers.
871
872
Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
873
A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags.
874
(The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse)
875
Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
876
- You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
877
- You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create
878
your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data.
879
880
Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
881
A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls".
882
(short version: map gamepad inputs into the io.NavInputs[] array + set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad)
883
- You can share your computer mouse seamlessly with your console/tablet/phone using Synergy (https://symless.com/synergy)
884
This is the preferred solution for developer productivity.
885
In particular, the "micro-synergy-client" repository (https://github.com/symless/micro-synergy-client) has simple
886
and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect
887
to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer.
888
Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols.
889
- You may also use a third party solution such as Remote ImGui (https://github.com/JordiRos/remoteimgui) which sends
890
the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine.
891
- For touch inputs, you can increase the hit box of widgets (via the style.TouchPadding setting) to accommodate
892
for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing
893
for screen real-estate and precision.
894
895
Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
896
A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
897
Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
898
899
Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
900
A: You are probably mishandling the clipping rectangles in your render function.
901
Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
902
903
Q: How can I help?
904
A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
905
and see how you want to help and can help!
906
- Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
907
- Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
908
- Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
909
You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1902). Visuals are ideal as they inspire other programmers.
910
But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
911
- 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).
912
913
- tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
914
this is also useful to set yourself in the context of another window (to get/set other settings)
915
- tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
916
- tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
917
of a deep nested inner loop in your code.
918
- tip: you can call Render() multiple times (e.g for VR renders).
919
- tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
920
921
*/
922
923
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
924
#define _CRT_SECURE_NO_WARNINGS
925
#endif
926
927
#include "imgui.h"
928
#ifndef IMGUI_DEFINE_MATH_OPERATORS
929
#define IMGUI_DEFINE_MATH_OPERATORS
930
#endif
931
#include "imgui_internal.h"
932
933
#include <ctype.h> // toupper, isprint
934
#include <stdio.h> // vsnprintf, sscanf, printf
935
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
936
#include <stddef.h> // intptr_t
937
#else
938
#include <stdint.h> // intptr_t
939
#endif
940
941
// Debug options
942
#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
943
#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
944
945
// Visual Studio warnings
946
#ifdef _MSC_VER
947
#pragma warning (disable: 4127) // condition expression is constant
948
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
949
#endif
950
951
// Clang/GCC warnings with -Weverything
952
#ifdef __clang__
953
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
954
#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
955
#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.
956
#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.
957
#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.
958
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
959
#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
960
#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.
961
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
962
#if __has_warning("-Wzero-as-null-pointer-constant")
963
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
964
#endif
965
#if __has_warning("-Wdouble-promotion")
966
#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.
967
#endif
968
#elif defined(__GNUC__)
969
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
970
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
971
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
972
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
973
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
974
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
975
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
976
#if __GNUC__ >= 8
977
#pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
978
#endif
979
#endif
980
981
// 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.
982
static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
983
static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
984
985
// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
986
static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
987
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
988
989
//-------------------------------------------------------------------------
990
// [SECTION] FORWARD DECLARATIONS
991
//-------------------------------------------------------------------------
992
993
static void SetCurrentWindow(ImGuiWindow* window);
994
static void FindHoveredWindow();
995
static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
996
static void CheckStacksSize(ImGuiWindow* window, bool write);
997
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
998
999
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
1000
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1001
1002
static ImRect GetViewportRect();
1003
1004
// Settings
1005
static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1006
static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1007
static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
1008
1009
// Platform Dependents default implementation for IO functions
1010
static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
1011
static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
1012
static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
1013
1014
namespace ImGui
1015
{
1016
static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
1017
1018
// Navigation
1019
static void NavUpdate();
1020
static void NavUpdateWindowing();
1021
static void NavUpdateWindowingList();
1022
static void NavUpdateMoveResult();
1023
static float NavUpdatePageUpPageDown(int allowed_dir_flags);
1024
static inline void NavUpdateAnyRequestFlag();
1025
static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
1026
static ImVec2 NavCalcPreferredRefPos();
1027
static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
1028
static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1029
1030
// Misc
1031
static void UpdateMouseInputs();
1032
static void UpdateMouseWheel();
1033
static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
1034
static void RenderOuterBorders(ImGuiWindow* window);
1035
1036
}
1037
1038
//-----------------------------------------------------------------------------
1039
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1040
//-----------------------------------------------------------------------------
1041
1042
// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1043
// ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
1044
// 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
1045
// SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
1046
// In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
1047
// 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
1048
// If you want thread-safety to allow N threads to access N different contexts, you can:
1049
// - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
1050
// struct ImGuiContext;
1051
// extern thread_local ImGuiContext* MyImGuiTLS;
1052
// #define GImGui MyImGuiTLS
1053
// And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
1054
// - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1055
// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
1056
#ifndef GImGui
1057
ImGuiContext* GImGui = NULL;
1058
#endif
1059
1060
// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1061
// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
1062
// Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
1063
#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1064
static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
1065
static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
1066
#else
1067
static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1068
static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1069
#endif
1070
1071
static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
1072
static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
1073
static void* GImAllocatorUserData = NULL;
1074
1075
//-----------------------------------------------------------------------------
1076
// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
1077
//-----------------------------------------------------------------------------
1078
1079
ImGuiStyle::ImGuiStyle()
1080
{
1081
Alpha = 1.0f; // Global alpha applies to everything in ImGui
1082
WindowPadding = ImVec2(8,8); // Padding within a window
1083
WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
1084
WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1085
WindowMinSize = ImVec2(32,32); // Minimum window size
1086
WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1087
ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1088
ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1089
PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1090
PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1091
FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1092
FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1093
FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1094
ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1095
ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1096
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!
1097
IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1098
ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
1099
ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1100
ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1101
GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
1102
GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1103
TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1104
TabBorderSize = 0.0f; // Thickness of border around tabs.
1105
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1106
SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
1107
DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
1108
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.
1109
MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1110
AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1111
AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1112
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.
1113
1114
// Default theme
1115
ImGui::StyleColorsDark(this);
1116
}
1117
1118
// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
1119
// 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.
1120
void ImGuiStyle::ScaleAllSizes(float scale_factor)
1121
{
1122
WindowPadding = ImFloor(WindowPadding * scale_factor);
1123
WindowRounding = ImFloor(WindowRounding * scale_factor);
1124
WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1125
ChildRounding = ImFloor(ChildRounding * scale_factor);
1126
PopupRounding = ImFloor(PopupRounding * scale_factor);
1127
FramePadding = ImFloor(FramePadding * scale_factor);
1128
FrameRounding = ImFloor(FrameRounding * scale_factor);
1129
ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1130
ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1131
TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1132
IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1133
ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1134
ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1135
ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1136
GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1137
GrabRounding = ImFloor(GrabRounding * scale_factor);
1138
TabRounding = ImFloor(TabRounding * scale_factor);
1139
DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1140
DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1141
MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1142
}
1143
1144
ImGuiIO::ImGuiIO()
1145
{
1146
// Most fields are initialized with zero
1147
memset(this, 0, sizeof(*this));
1148
1149
// Settings
1150
ConfigFlags = ImGuiConfigFlags_None;
1151
BackendFlags = ImGuiBackendFlags_None;
1152
DisplaySize = ImVec2(-1.0f, -1.0f);
1153
DeltaTime = 1.0f/60.0f;
1154
IniSavingRate = 5.0f;
1155
IniFilename = "imgui.ini";
1156
LogFilename = "imgui_log.txt";
1157
MouseDoubleClickTime = 0.30f;
1158
MouseDoubleClickMaxDist = 6.0f;
1159
for (int i = 0; i < ImGuiKey_COUNT; i++)
1160
KeyMap[i] = -1;
1161
KeyRepeatDelay = 0.250f;
1162
KeyRepeatRate = 0.050f;
1163
UserData = NULL;
1164
1165
Fonts = NULL;
1166
FontGlobalScale = 1.0f;
1167
FontDefault = NULL;
1168
FontAllowUserScaling = false;
1169
DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1170
1171
// Miscellaneous options
1172
MouseDrawCursor = false;
1173
#ifdef __APPLE__
1174
ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1175
#else
1176
ConfigMacOSXBehaviors = false;
1177
#endif
1178
ConfigInputTextCursorBlink = true;
1179
ConfigWindowsResizeFromEdges = true;
1180
ConfigWindowsMoveFromTitleBarOnly = false;
1181
1182
// Platform Functions
1183
BackendPlatformName = BackendRendererName = NULL;
1184
BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1185
GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1186
SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1187
ClipboardUserData = NULL;
1188
ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1189
ImeWindowHandle = NULL;
1190
1191
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1192
RenderDrawListsFn = NULL;
1193
#endif
1194
1195
// Input (NB: we already have memset zero the entire structure!)
1196
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1197
MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1198
MouseDragThreshold = 6.0f;
1199
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1200
for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1201
for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1202
}
1203
1204
// Pass in translated ASCII characters for text input.
1205
// - with glfw you can get those from the callback set in glfwSetCharCallback()
1206
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1207
void ImGuiIO::AddInputCharacter(ImWchar c)
1208
{
1209
InputQueueCharacters.push_back(c);
1210
}
1211
1212
void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1213
{
1214
while (*utf8_chars != 0)
1215
{
1216
unsigned int c = 0;
1217
utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1218
if (c > 0 && c <= 0xFFFF)
1219
InputQueueCharacters.push_back((ImWchar)c);
1220
}
1221
}
1222
1223
void ImGuiIO::ClearInputCharacters()
1224
{
1225
InputQueueCharacters.resize(0);
1226
}
1227
1228
//-----------------------------------------------------------------------------
1229
// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1230
//-----------------------------------------------------------------------------
1231
1232
ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1233
{
1234
ImVec2 ap = p - a;
1235
ImVec2 ab_dir = b - a;
1236
float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1237
if (dot < 0.0f)
1238
return a;
1239
float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1240
if (dot > ab_len_sqr)
1241
return b;
1242
return a + ab_dir * dot / ab_len_sqr;
1243
}
1244
1245
bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1246
{
1247
bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1248
bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1249
bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1250
return ((b1 == b2) && (b2 == b3));
1251
}
1252
1253
void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1254
{
1255
ImVec2 v0 = b - a;
1256
ImVec2 v1 = c - a;
1257
ImVec2 v2 = p - a;
1258
const float denom = v0.x * v1.y - v1.x * v0.y;
1259
out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1260
out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1261
out_u = 1.0f - out_v - out_w;
1262
}
1263
1264
ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1265
{
1266
ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1267
ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1268
ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1269
float dist2_ab = ImLengthSqr(p - proj_ab);
1270
float dist2_bc = ImLengthSqr(p - proj_bc);
1271
float dist2_ca = ImLengthSqr(p - proj_ca);
1272
float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1273
if (m == dist2_ab)
1274
return proj_ab;
1275
if (m == dist2_bc)
1276
return proj_bc;
1277
return proj_ca;
1278
}
1279
1280
// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
1281
int ImStricmp(const char* str1, const char* str2)
1282
{
1283
int d;
1284
while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1285
return d;
1286
}
1287
1288
int ImStrnicmp(const char* str1, const char* str2, size_t count)
1289
{
1290
int d = 0;
1291
while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1292
return d;
1293
}
1294
1295
void ImStrncpy(char* dst, const char* src, size_t count)
1296
{
1297
if (count < 1)
1298
return;
1299
if (count > 1)
1300
strncpy(dst, src, count - 1);
1301
dst[count - 1] = 0;
1302
}
1303
1304
char* ImStrdup(const char* str)
1305
{
1306
size_t len = strlen(str);
1307
void* buf = ImGui::MemAlloc(len + 1);
1308
return (char*)memcpy(buf, (const void*)str, len + 1);
1309
}
1310
1311
char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1312
{
1313
size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1314
size_t src_size = strlen(src) + 1;
1315
if (dst_buf_size < src_size)
1316
{
1317
ImGui::MemFree(dst);
1318
dst = (char*)ImGui::MemAlloc(src_size);
1319
if (p_dst_size)
1320
*p_dst_size = src_size;
1321
}
1322
return (char*)memcpy(dst, (const void*)src, src_size);
1323
}
1324
1325
const char* ImStrchrRange(const char* str, const char* str_end, char c)
1326
{
1327
const char* p = (const char*)memchr(str, (int)c, str_end - str);
1328
return p;
1329
}
1330
1331
int ImStrlenW(const ImWchar* str)
1332
{
1333
//return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bits
1334
int n = 0;
1335
while (*str++) n++;
1336
return n;
1337
}
1338
1339
// Find end-of-line. Return pointer will point to either first \n, either str_end.
1340
const char* ImStreolRange(const char* str, const char* str_end)
1341
{
1342
const char* p = (const char*)memchr(str, '\n', str_end - str);
1343
return p ? p : str_end;
1344
}
1345
1346
const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1347
{
1348
while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1349
buf_mid_line--;
1350
return buf_mid_line;
1351
}
1352
1353
const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1354
{
1355
if (!needle_end)
1356
needle_end = needle + strlen(needle);
1357
1358
const char un0 = (char)toupper(*needle);
1359
while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1360
{
1361
if (toupper(*haystack) == un0)
1362
{
1363
const char* b = needle + 1;
1364
for (const char* a = haystack + 1; b < needle_end; a++, b++)
1365
if (toupper(*a) != toupper(*b))
1366
break;
1367
if (b == needle_end)
1368
return haystack;
1369
}
1370
haystack++;
1371
}
1372
return NULL;
1373
}
1374
1375
// 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.
1376
void ImStrTrimBlanks(char* buf)
1377
{
1378
char* p = buf;
1379
while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1380
p++;
1381
char* p_start = p;
1382
while (*p != 0) // Find end of string
1383
p++;
1384
while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1385
p--;
1386
if (p_start != buf) // Copy memory if we had leading blanks
1387
memmove(buf, p_start, p - p_start);
1388
buf[p - p_start] = 0; // Zero terminate
1389
}
1390
1391
// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1392
// 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.
1393
// B) When buf==NULL vsnprintf() will return the output size.
1394
#ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1395
1396
//#define IMGUI_USE_STB_SPRINTF
1397
#ifdef IMGUI_USE_STB_SPRINTF
1398
#define STB_SPRINTF_IMPLEMENTATION
1399
#include "imstb_sprintf.h"
1400
#endif
1401
1402
#if defined(_MSC_VER) && !defined(vsnprintf)
1403
#define vsnprintf _vsnprintf
1404
#endif
1405
1406
int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1407
{
1408
va_list args;
1409
va_start(args, fmt);
1410
#ifdef IMGUI_USE_STB_SPRINTF
1411
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1412
#else
1413
int w = vsnprintf(buf, buf_size, fmt, args);
1414
#endif
1415
va_end(args);
1416
if (buf == NULL)
1417
return w;
1418
if (w == -1 || w >= (int)buf_size)
1419
w = (int)buf_size - 1;
1420
buf[w] = 0;
1421
return w;
1422
}
1423
1424
int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1425
{
1426
#ifdef IMGUI_USE_STB_SPRINTF
1427
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1428
#else
1429
int w = vsnprintf(buf, buf_size, fmt, args);
1430
#endif
1431
if (buf == NULL)
1432
return w;
1433
if (w == -1 || w >= (int)buf_size)
1434
w = (int)buf_size - 1;
1435
buf[w] = 0;
1436
return w;
1437
}
1438
#endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1439
1440
// CRC32 needs a 1KB lookup table (not cache friendly)
1441
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1442
// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1443
static const ImU32 GCrc32LookupTable[256] =
1444
{
1445
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1446
0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1447
0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1448
0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1449
0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1450
0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1451
0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1452
0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1453
0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1454
0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1455
0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1456
0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1457
0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1458
0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1459
0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1460
0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1461
};
1462
1463
// Known size hash
1464
// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1465
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1466
ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1467
{
1468
ImU32 crc = ~seed;
1469
const unsigned char* data = (const unsigned char*)data_p;
1470
const ImU32* crc32_lut = GCrc32LookupTable;
1471
while (data_size-- != 0)
1472
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1473
return ~crc;
1474
}
1475
1476
// Zero-terminated string hash, with support for ### to reset back to seed value
1477
// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1478
// Because this syntax is rarely used we are optimizing for the common case.
1479
// - If we reach ### in the string we discard the hash so far and reset to the seed.
1480
// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1481
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1482
ImU32 ImHashStr(const char* data, size_t data_size, ImU32 seed)
1483
{
1484
seed = ~seed;
1485
ImU32 crc = seed;
1486
const unsigned char* src = (const unsigned char*)data;
1487
const ImU32* crc32_lut = GCrc32LookupTable;
1488
if (data_size != 0)
1489
{
1490
while (data_size-- != 0)
1491
{
1492
unsigned char c = *src++;
1493
if (c == '#' && src[0] == '#' && src[1] == '#')
1494
crc = seed;
1495
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1496
}
1497
}
1498
else
1499
{
1500
while (unsigned char c = *src++)
1501
{
1502
if (c == '#' && src[0] == '#' && src[1] == '#')
1503
crc = seed;
1504
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1505
}
1506
}
1507
return ~crc;
1508
}
1509
1510
FILE* ImFileOpen(const char* filename, const char* mode)
1511
{
1512
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1513
// We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1514
const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1515
const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1516
ImVector<ImWchar> buf;
1517
buf.resize(filename_wsize + mode_wsize);
1518
ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1519
ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1520
return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1521
#else
1522
return fopen(filename, mode);
1523
#endif
1524
}
1525
1526
// Load file content into memory
1527
// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
1528
void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1529
{
1530
IM_ASSERT(filename && file_open_mode);
1531
if (out_file_size)
1532
*out_file_size = 0;
1533
1534
FILE* f;
1535
if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1536
return NULL;
1537
1538
long file_size_signed;
1539
if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1540
{
1541
fclose(f);
1542
return NULL;
1543
}
1544
1545
size_t file_size = (size_t)file_size_signed;
1546
void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1547
if (file_data == NULL)
1548
{
1549
fclose(f);
1550
return NULL;
1551
}
1552
if (fread(file_data, 1, file_size, f) != file_size)
1553
{
1554
fclose(f);
1555
ImGui::MemFree(file_data);
1556
return NULL;
1557
}
1558
if (padding_bytes > 0)
1559
memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1560
1561
fclose(f);
1562
if (out_file_size)
1563
*out_file_size = file_size;
1564
1565
return file_data;
1566
}
1567
1568
//-----------------------------------------------------------------------------
1569
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1570
//-----------------------------------------------------------------------------
1571
1572
// Convert UTF-8 to 32-bits character, process single character input.
1573
// Based on stb_from_utf8() from github.com/nothings/stb/
1574
// We handle UTF-8 decoding error by skipping forward.
1575
int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1576
{
1577
unsigned int c = (unsigned int)-1;
1578
const unsigned char* str = (const unsigned char*)in_text;
1579
if (!(*str & 0x80))
1580
{
1581
c = (unsigned int)(*str++);
1582
*out_char = c;
1583
return 1;
1584
}
1585
if ((*str & 0xe0) == 0xc0)
1586
{
1587
*out_char = 0xFFFD; // will be invalid but not end of string
1588
if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1589
if (*str < 0xc2) return 2;
1590
c = (unsigned int)((*str++ & 0x1f) << 6);
1591
if ((*str & 0xc0) != 0x80) return 2;
1592
c += (*str++ & 0x3f);
1593
*out_char = c;
1594
return 2;
1595
}
1596
if ((*str & 0xf0) == 0xe0)
1597
{
1598
*out_char = 0xFFFD; // will be invalid but not end of string
1599
if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1600
if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1601
if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1602
c = (unsigned int)((*str++ & 0x0f) << 12);
1603
if ((*str & 0xc0) != 0x80) return 3;
1604
c += (unsigned int)((*str++ & 0x3f) << 6);
1605
if ((*str & 0xc0) != 0x80) return 3;
1606
c += (*str++ & 0x3f);
1607
*out_char = c;
1608
return 3;
1609
}
1610
if ((*str & 0xf8) == 0xf0)
1611
{
1612
*out_char = 0xFFFD; // will be invalid but not end of string
1613
if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1614
if (*str > 0xf4) return 4;
1615
if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1616
if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1617
c = (unsigned int)((*str++ & 0x07) << 18);
1618
if ((*str & 0xc0) != 0x80) return 4;
1619
c += (unsigned int)((*str++ & 0x3f) << 12);
1620
if ((*str & 0xc0) != 0x80) return 4;
1621
c += (unsigned int)((*str++ & 0x3f) << 6);
1622
if ((*str & 0xc0) != 0x80) return 4;
1623
c += (*str++ & 0x3f);
1624
// utf-8 encodings of values used in surrogate pairs are invalid
1625
if ((c & 0xFFFFF800) == 0xD800) return 4;
1626
*out_char = c;
1627
return 4;
1628
}
1629
*out_char = 0;
1630
return 0;
1631
}
1632
1633
int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1634
{
1635
ImWchar* buf_out = buf;
1636
ImWchar* buf_end = buf + buf_size;
1637
while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1638
{
1639
unsigned int c;
1640
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1641
if (c == 0)
1642
break;
1643
if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1644
*buf_out++ = (ImWchar)c;
1645
}
1646
*buf_out = 0;
1647
if (in_text_remaining)
1648
*in_text_remaining = in_text;
1649
return (int)(buf_out - buf);
1650
}
1651
1652
int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1653
{
1654
int char_count = 0;
1655
while ((!in_text_end || in_text < in_text_end) && *in_text)
1656
{
1657
unsigned int c;
1658
in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1659
if (c == 0)
1660
break;
1661
if (c < 0x10000)
1662
char_count++;
1663
}
1664
return char_count;
1665
}
1666
1667
// Based on stb_to_utf8() from github.com/nothings/stb/
1668
static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1669
{
1670
if (c < 0x80)
1671
{
1672
buf[0] = (char)c;
1673
return 1;
1674
}
1675
if (c < 0x800)
1676
{
1677
if (buf_size < 2) return 0;
1678
buf[0] = (char)(0xc0 + (c >> 6));
1679
buf[1] = (char)(0x80 + (c & 0x3f));
1680
return 2;
1681
}
1682
if (c >= 0xdc00 && c < 0xe000)
1683
{
1684
return 0;
1685
}
1686
if (c >= 0xd800 && c < 0xdc00)
1687
{
1688
if (buf_size < 4) return 0;
1689
buf[0] = (char)(0xf0 + (c >> 18));
1690
buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1691
buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1692
buf[3] = (char)(0x80 + ((c ) & 0x3f));
1693
return 4;
1694
}
1695
//else if (c < 0x10000)
1696
{
1697
if (buf_size < 3) return 0;
1698
buf[0] = (char)(0xe0 + (c >> 12));
1699
buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1700
buf[2] = (char)(0x80 + ((c ) & 0x3f));
1701
return 3;
1702
}
1703
}
1704
1705
// Not optimal but we very rarely use this function.
1706
int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1707
{
1708
unsigned int dummy = 0;
1709
return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1710
}
1711
1712
static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1713
{
1714
if (c < 0x80) return 1;
1715
if (c < 0x800) return 2;
1716
if (c >= 0xdc00 && c < 0xe000) return 0;
1717
if (c >= 0xd800 && c < 0xdc00) return 4;
1718
return 3;
1719
}
1720
1721
int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1722
{
1723
char* buf_out = buf;
1724
const char* buf_end = buf + buf_size;
1725
while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1726
{
1727
unsigned int c = (unsigned int)(*in_text++);
1728
if (c < 0x80)
1729
*buf_out++ = (char)c;
1730
else
1731
buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1732
}
1733
*buf_out = 0;
1734
return (int)(buf_out - buf);
1735
}
1736
1737
int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1738
{
1739
int bytes_count = 0;
1740
while ((!in_text_end || in_text < in_text_end) && *in_text)
1741
{
1742
unsigned int c = (unsigned int)(*in_text++);
1743
if (c < 0x80)
1744
bytes_count++;
1745
else
1746
bytes_count += ImTextCountUtf8BytesFromChar(c);
1747
}
1748
return bytes_count;
1749
}
1750
1751
//-----------------------------------------------------------------------------
1752
// [SECTION] MISC HELPER/UTILTIES (Color functions)
1753
// Note: The Convert functions are early design which are not consistent with other API.
1754
//-----------------------------------------------------------------------------
1755
1756
ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1757
{
1758
float s = 1.0f/255.0f;
1759
return ImVec4(
1760
((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1761
((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1762
((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1763
((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1764
}
1765
1766
ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1767
{
1768
ImU32 out;
1769
out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1770
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1771
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1772
out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1773
return out;
1774
}
1775
1776
// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1777
// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1778
void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1779
{
1780
float K = 0.f;
1781
if (g < b)
1782
{
1783
ImSwap(g, b);
1784
K = -1.f;
1785
}
1786
if (r < g)
1787
{
1788
ImSwap(r, g);
1789
K = -2.f / 6.f - K;
1790
}
1791
1792
const float chroma = r - (g < b ? g : b);
1793
out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1794
out_s = chroma / (r + 1e-20f);
1795
out_v = r;
1796
}
1797
1798
// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1799
// also http://en.wikipedia.org/wiki/HSL_and_HSV
1800
void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1801
{
1802
if (s == 0.0f)
1803
{
1804
// gray
1805
out_r = out_g = out_b = v;
1806
return;
1807
}
1808
1809
h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1810
int i = (int)h;
1811
float f = h - (float)i;
1812
float p = v * (1.0f - s);
1813
float q = v * (1.0f - s * f);
1814
float t = v * (1.0f - s * (1.0f - f));
1815
1816
switch (i)
1817
{
1818
case 0: out_r = v; out_g = t; out_b = p; break;
1819
case 1: out_r = q; out_g = v; out_b = p; break;
1820
case 2: out_r = p; out_g = v; out_b = t; break;
1821
case 3: out_r = p; out_g = q; out_b = v; break;
1822
case 4: out_r = t; out_g = p; out_b = v; break;
1823
case 5: default: out_r = v; out_g = p; out_b = q; break;
1824
}
1825
}
1826
1827
ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1828
{
1829
ImGuiStyle& style = GImGui->Style;
1830
ImVec4 c = style.Colors[idx];
1831
c.w *= style.Alpha * alpha_mul;
1832
return ColorConvertFloat4ToU32(c);
1833
}
1834
1835
ImU32 ImGui::GetColorU32(const ImVec4& col)
1836
{
1837
ImGuiStyle& style = GImGui->Style;
1838
ImVec4 c = col;
1839
c.w *= style.Alpha;
1840
return ColorConvertFloat4ToU32(c);
1841
}
1842
1843
const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1844
{
1845
ImGuiStyle& style = GImGui->Style;
1846
return style.Colors[idx];
1847
}
1848
1849
ImU32 ImGui::GetColorU32(ImU32 col)
1850
{
1851
float style_alpha = GImGui->Style.Alpha;
1852
if (style_alpha >= 1.0f)
1853
return col;
1854
ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1855
a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1856
return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1857
}
1858
1859
//-----------------------------------------------------------------------------
1860
// [SECTION] ImGuiStorage
1861
// Helper: Key->value storage
1862
//-----------------------------------------------------------------------------
1863
1864
// std::lower_bound but without the bullshit
1865
static ImGuiStorage::Pair* LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1866
{
1867
ImGuiStorage::Pair* first = data.Data;
1868
ImGuiStorage::Pair* last = data.Data + data.Size;
1869
size_t count = (size_t)(last - first);
1870
while (count > 0)
1871
{
1872
size_t count2 = count >> 1;
1873
ImGuiStorage::Pair* mid = first + count2;
1874
if (mid->key < key)
1875
{
1876
first = ++mid;
1877
count -= count2 + 1;
1878
}
1879
else
1880
{
1881
count = count2;
1882
}
1883
}
1884
return first;
1885
}
1886
1887
// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1888
void ImGuiStorage::BuildSortByKey()
1889
{
1890
struct StaticFunc
1891
{
1892
static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1893
{
1894
// We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1895
if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1896
if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1897
return 0;
1898
}
1899
};
1900
if (Data.Size > 1)
1901
ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1902
}
1903
1904
int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1905
{
1906
ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1907
if (it == Data.end() || it->key != key)
1908
return default_val;
1909
return it->val_i;
1910
}
1911
1912
bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1913
{
1914
return GetInt(key, default_val ? 1 : 0) != 0;
1915
}
1916
1917
float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1918
{
1919
ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1920
if (it == Data.end() || it->key != key)
1921
return default_val;
1922
return it->val_f;
1923
}
1924
1925
void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1926
{
1927
ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1928
if (it == Data.end() || it->key != key)
1929
return NULL;
1930
return it->val_p;
1931
}
1932
1933
// 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.
1934
int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1935
{
1936
ImGuiStorage::Pair* it = LowerBound(Data, key);
1937
if (it == Data.end() || it->key != key)
1938
it = Data.insert(it, Pair(key, default_val));
1939
return &it->val_i;
1940
}
1941
1942
bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1943
{
1944
return (bool*)GetIntRef(key, default_val ? 1 : 0);
1945
}
1946
1947
float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1948
{
1949
ImGuiStorage::Pair* it = LowerBound(Data, key);
1950
if (it == Data.end() || it->key != key)
1951
it = Data.insert(it, Pair(key, default_val));
1952
return &it->val_f;
1953
}
1954
1955
void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1956
{
1957
ImGuiStorage::Pair* it = LowerBound(Data, key);
1958
if (it == Data.end() || it->key != key)
1959
it = Data.insert(it, Pair(key, default_val));
1960
return &it->val_p;
1961
}
1962
1963
// 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)
1964
void ImGuiStorage::SetInt(ImGuiID key, int val)
1965
{
1966
ImGuiStorage::Pair* it = LowerBound(Data, key);
1967
if (it == Data.end() || it->key != key)
1968
{
1969
Data.insert(it, Pair(key, val));
1970
return;
1971
}
1972
it->val_i = val;
1973
}
1974
1975
void ImGuiStorage::SetBool(ImGuiID key, bool val)
1976
{
1977
SetInt(key, val ? 1 : 0);
1978
}
1979
1980
void ImGuiStorage::SetFloat(ImGuiID key, float val)
1981
{
1982
ImGuiStorage::Pair* it = LowerBound(Data, key);
1983
if (it == Data.end() || it->key != key)
1984
{
1985
Data.insert(it, Pair(key, val));
1986
return;
1987
}
1988
it->val_f = val;
1989
}
1990
1991
void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1992
{
1993
ImGuiStorage::Pair* it = LowerBound(Data, key);
1994
if (it == Data.end() || it->key != key)
1995
{
1996
Data.insert(it, Pair(key, val));
1997
return;
1998
}
1999
it->val_p = val;
2000
}
2001
2002
void ImGuiStorage::SetAllInt(int v)
2003
{
2004
for (int i = 0; i < Data.Size; i++)
2005
Data[i].val_i = v;
2006
}
2007
2008
//-----------------------------------------------------------------------------
2009
// [SECTION] ImGuiTextFilter
2010
//-----------------------------------------------------------------------------
2011
2012
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2013
ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2014
{
2015
if (default_filter)
2016
{
2017
ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2018
Build();
2019
}
2020
else
2021
{
2022
InputBuf[0] = 0;
2023
CountGrep = 0;
2024
}
2025
}
2026
2027
bool ImGuiTextFilter::Draw(const char* label, float width)
2028
{
2029
if (width != 0.0f)
2030
ImGui::PushItemWidth(width);
2031
bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2032
if (width != 0.0f)
2033
ImGui::PopItemWidth();
2034
if (value_changed)
2035
Build();
2036
return value_changed;
2037
}
2038
2039
void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
2040
{
2041
out->resize(0);
2042
const char* wb = b;
2043
const char* we = wb;
2044
while (we < e)
2045
{
2046
if (*we == separator)
2047
{
2048
out->push_back(TextRange(wb, we));
2049
wb = we + 1;
2050
}
2051
we++;
2052
}
2053
if (wb != we)
2054
out->push_back(TextRange(wb, we));
2055
}
2056
2057
void ImGuiTextFilter::Build()
2058
{
2059
Filters.resize(0);
2060
TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
2061
input_range.split(',', &Filters);
2062
2063
CountGrep = 0;
2064
for (int i = 0; i != Filters.Size; i++)
2065
{
2066
TextRange& f = Filters[i];
2067
while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2068
f.b++;
2069
while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2070
f.e--;
2071
if (f.empty())
2072
continue;
2073
if (Filters[i].b[0] != '-')
2074
CountGrep += 1;
2075
}
2076
}
2077
2078
bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2079
{
2080
if (Filters.empty())
2081
return true;
2082
2083
if (text == NULL)
2084
text = "";
2085
2086
for (int i = 0; i != Filters.Size; i++)
2087
{
2088
const TextRange& f = Filters[i];
2089
if (f.empty())
2090
continue;
2091
if (f.b[0] == '-')
2092
{
2093
// Subtract
2094
if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
2095
return false;
2096
}
2097
else
2098
{
2099
// Grep
2100
if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
2101
return true;
2102
}
2103
}
2104
2105
// Implicit * grep
2106
if (CountGrep == 0)
2107
return true;
2108
2109
return false;
2110
}
2111
2112
//-----------------------------------------------------------------------------
2113
// [SECTION] ImGuiTextBuffer
2114
//-----------------------------------------------------------------------------
2115
2116
// On some platform vsnprintf() takes va_list by reference and modifies it.
2117
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2118
#ifndef va_copy
2119
#if defined(__GNUC__) || defined(__clang__)
2120
#define va_copy(dest, src) __builtin_va_copy(dest, src)
2121
#else
2122
#define va_copy(dest, src) (dest = src)
2123
#endif
2124
#endif
2125
2126
char ImGuiTextBuffer::EmptyString[1] = { 0 };
2127
2128
void ImGuiTextBuffer::append(const char* str, const char* str_end)
2129
{
2130
int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2131
2132
// Add zero-terminator the first time
2133
const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2134
const int needed_sz = write_off + len;
2135
if (write_off + len >= Buf.Capacity)
2136
{
2137
int new_capacity = Buf.Capacity * 2;
2138
Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2139
}
2140
2141
Buf.resize(needed_sz);
2142
memcpy(&Buf[write_off - 1], str, (size_t)len);
2143
Buf[write_off - 1 + len] = 0;
2144
}
2145
2146
void ImGuiTextBuffer::appendf(const char* fmt, ...)
2147
{
2148
va_list args;
2149
va_start(args, fmt);
2150
appendfv(fmt, args);
2151
va_end(args);
2152
}
2153
2154
// Helper: Text buffer for logging/accumulating text
2155
void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2156
{
2157
va_list args_copy;
2158
va_copy(args_copy, args);
2159
2160
int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2161
if (len <= 0)
2162
{
2163
va_end(args_copy);
2164
return;
2165
}
2166
2167
// Add zero-terminator the first time
2168
const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2169
const int needed_sz = write_off + len;
2170
if (write_off + len >= Buf.Capacity)
2171
{
2172
int new_capacity = Buf.Capacity * 2;
2173
Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2174
}
2175
2176
Buf.resize(needed_sz);
2177
ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2178
va_end(args_copy);
2179
}
2180
2181
//-----------------------------------------------------------------------------
2182
// [SECTION] ImGuiListClipper
2183
// This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
2184
//-----------------------------------------------------------------------------
2185
2186
static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2187
{
2188
// Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2189
// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2190
// The clipper should probably have a 4th step to display the last item in a regular manner.
2191
ImGui::SetCursorPosY(pos_y);
2192
ImGuiWindow* window = ImGui::GetCurrentWindow();
2193
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.
2194
window->DC.PrevLineSize.y = (line_height - GImGui->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.
2195
if (window->DC.ColumnsSet)
2196
window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2197
}
2198
2199
// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2200
// Use case B: Begin() called from constructor with items_height>0
2201
// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
2202
void ImGuiListClipper::Begin(int count, float items_height)
2203
{
2204
StartPosY = ImGui::GetCursorPosY();
2205
ItemsHeight = items_height;
2206
ItemsCount = count;
2207
StepNo = 0;
2208
DisplayEnd = DisplayStart = -1;
2209
if (ItemsHeight > 0.0f)
2210
{
2211
ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2212
if (DisplayStart > 0)
2213
SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2214
StepNo = 2;
2215
}
2216
}
2217
2218
void ImGuiListClipper::End()
2219
{
2220
if (ItemsCount < 0)
2221
return;
2222
// In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
2223
if (ItemsCount < INT_MAX)
2224
SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2225
ItemsCount = -1;
2226
StepNo = 3;
2227
}
2228
2229
bool ImGuiListClipper::Step()
2230
{
2231
if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2232
{
2233
ItemsCount = -1;
2234
return false;
2235
}
2236
if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2237
{
2238
DisplayStart = 0;
2239
DisplayEnd = 1;
2240
StartPosY = ImGui::GetCursorPosY();
2241
StepNo = 1;
2242
return true;
2243
}
2244
if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2245
{
2246
if (ItemsCount == 1) { ItemsCount = -1; return false; }
2247
float items_height = ImGui::GetCursorPosY() - StartPosY;
2248
IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2249
Begin(ItemsCount-1, items_height);
2250
DisplayStart++;
2251
DisplayEnd++;
2252
StepNo = 3;
2253
return true;
2254
}
2255
if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2256
{
2257
IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2258
StepNo = 3;
2259
return true;
2260
}
2261
if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2262
End();
2263
return false;
2264
}
2265
2266
//-----------------------------------------------------------------------------
2267
// [SECTION] RENDER HELPERS
2268
// Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2269
// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2270
//-----------------------------------------------------------------------------
2271
2272
const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2273
{
2274
const char* text_display_end = text;
2275
if (!text_end)
2276
text_end = (const char*)-1;
2277
2278
while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2279
text_display_end++;
2280
return text_display_end;
2281
}
2282
2283
// Internal ImGui functions to render text
2284
// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
2285
void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2286
{
2287
ImGuiContext& g = *GImGui;
2288
ImGuiWindow* window = g.CurrentWindow;
2289
2290
// Hide anything after a '##' string
2291
const char* text_display_end;
2292
if (hide_text_after_hash)
2293
{
2294
text_display_end = FindRenderedTextEnd(text, text_end);
2295
}
2296
else
2297
{
2298
if (!text_end)
2299
text_end = text + strlen(text); // FIXME-OPT
2300
text_display_end = text_end;
2301
}
2302
2303
if (text != text_display_end)
2304
{
2305
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2306
if (g.LogEnabled)
2307
LogRenderedText(&pos, text, text_display_end);
2308
}
2309
}
2310
2311
void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2312
{
2313
ImGuiContext& g = *GImGui;
2314
ImGuiWindow* window = g.CurrentWindow;
2315
2316
if (!text_end)
2317
text_end = text + strlen(text); // FIXME-OPT
2318
2319
if (text != text_end)
2320
{
2321
window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2322
if (g.LogEnabled)
2323
LogRenderedText(&pos, text, text_end);
2324
}
2325
}
2326
2327
// Default clip_rect uses (pos_min,pos_max)
2328
// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
2329
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)
2330
{
2331
// Perform CPU side clipping for single clipped element to avoid using scissor state
2332
ImVec2 pos = pos_min;
2333
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2334
2335
const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2336
const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2337
bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2338
if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2339
need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2340
2341
// Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2342
if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2343
if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2344
2345
// Render
2346
if (need_clipping)
2347
{
2348
ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2349
draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2350
}
2351
else
2352
{
2353
draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2354
}
2355
}
2356
2357
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)
2358
{
2359
// Hide anything after a '##' string
2360
const char* text_display_end = FindRenderedTextEnd(text, text_end);
2361
const int text_len = (int)(text_display_end - text);
2362
if (text_len == 0)
2363
return;
2364
2365
ImGuiContext& g = *GImGui;
2366
ImGuiWindow* window = g.CurrentWindow;
2367
RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2368
if (g.LogEnabled)
2369
LogRenderedText(&pos_min, text, text_display_end);
2370
}
2371
2372
// Render a rectangle shaped with optional rounding and borders
2373
void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2374
{
2375
ImGuiContext& g = *GImGui;
2376
ImGuiWindow* window = g.CurrentWindow;
2377
window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2378
const float border_size = g.Style.FrameBorderSize;
2379
if (border && border_size > 0.0f)
2380
{
2381
window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2382
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2383
}
2384
}
2385
2386
void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2387
{
2388
ImGuiContext& g = *GImGui;
2389
ImGuiWindow* window = g.CurrentWindow;
2390
const float border_size = g.Style.FrameBorderSize;
2391
if (border_size > 0.0f)
2392
{
2393
window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2394
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2395
}
2396
}
2397
2398
// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
2399
void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2400
{
2401
ImGuiContext& g = *GImGui;
2402
2403
const float h = g.FontSize * 1.00f;
2404
float r = h * 0.40f * scale;
2405
ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2406
2407
ImVec2 a, b, c;
2408
switch (dir)
2409
{
2410
case ImGuiDir_Up:
2411
case ImGuiDir_Down:
2412
if (dir == ImGuiDir_Up) r = -r;
2413
a = ImVec2(+0.000f,+0.750f) * r;
2414
b = ImVec2(-0.866f,-0.750f) * r;
2415
c = ImVec2(+0.866f,-0.750f) * r;
2416
break;
2417
case ImGuiDir_Left:
2418
case ImGuiDir_Right:
2419
if (dir == ImGuiDir_Left) r = -r;
2420
a = ImVec2(+0.750f,+0.000f) * r;
2421
b = ImVec2(-0.750f,+0.866f) * r;
2422
c = ImVec2(-0.750f,-0.866f) * r;
2423
break;
2424
case ImGuiDir_None:
2425
case ImGuiDir_COUNT:
2426
IM_ASSERT(0);
2427
break;
2428
}
2429
2430
g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2431
}
2432
2433
void ImGui::RenderBullet(ImVec2 pos)
2434
{
2435
ImGuiContext& g = *GImGui;
2436
ImGuiWindow* window = g.CurrentWindow;
2437
window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2438
}
2439
2440
void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2441
{
2442
ImGuiContext& g = *GImGui;
2443
ImGuiWindow* window = g.CurrentWindow;
2444
2445
float thickness = ImMax(sz / 5.0f, 1.0f);
2446
sz -= thickness*0.5f;
2447
pos += ImVec2(thickness*0.25f, thickness*0.25f);
2448
2449
float third = sz / 3.0f;
2450
float bx = pos.x + third;
2451
float by = pos.y + sz - third*0.5f;
2452
window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2453
window->DrawList->PathLineTo(ImVec2(bx, by));
2454
window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2455
window->DrawList->PathStroke(col, false, thickness);
2456
}
2457
2458
void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2459
{
2460
ImGuiContext& g = *GImGui;
2461
if (id != g.NavId)
2462
return;
2463
if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2464
return;
2465
ImGuiWindow* window = g.CurrentWindow;
2466
if (window->DC.NavHideHighlightOneFrame)
2467
return;
2468
2469
float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2470
ImRect display_rect = bb;
2471
display_rect.ClipWith(window->ClipRect);
2472
if (flags & ImGuiNavHighlightFlags_TypeDefault)
2473
{
2474
const float THICKNESS = 2.0f;
2475
const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2476
display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2477
bool fully_visible = window->ClipRect.Contains(display_rect);
2478
if (!fully_visible)
2479
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2480
window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
2481
if (!fully_visible)
2482
window->DrawList->PopClipRect();
2483
}
2484
if (flags & ImGuiNavHighlightFlags_TypeThin)
2485
{
2486
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2487
}
2488
}
2489
2490
//-----------------------------------------------------------------------------
2491
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2492
//-----------------------------------------------------------------------------
2493
2494
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
2495
ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2496
: DrawListInst(&context->DrawListSharedData)
2497
{
2498
Name = ImStrdup(name);
2499
ID = ImHashStr(name, 0);
2500
IDStack.push_back(ID);
2501
Flags = ImGuiWindowFlags_None;
2502
Pos = ImVec2(0.0f, 0.0f);
2503
Size = SizeFull = ImVec2(0.0f, 0.0f);
2504
SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2505
WindowPadding = ImVec2(0.0f, 0.0f);
2506
WindowRounding = 0.0f;
2507
WindowBorderSize = 0.0f;
2508
NameBufLen = (int)strlen(name) + 1;
2509
MoveId = GetID("#MOVE");
2510
ChildId = 0;
2511
Scroll = ImVec2(0.0f, 0.0f);
2512
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2513
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2514
ScrollbarSizes = ImVec2(0.0f, 0.0f);
2515
ScrollbarX = ScrollbarY = false;
2516
Active = WasActive = false;
2517
WriteAccessed = false;
2518
Collapsed = false;
2519
WantCollapseToggle = false;
2520
SkipItems = false;
2521
Appearing = false;
2522
Hidden = false;
2523
HasCloseButton = false;
2524
ResizeBorderHeld = -1;
2525
BeginCount = 0;
2526
BeginOrderWithinParent = -1;
2527
BeginOrderWithinContext = -1;
2528
PopupId = 0;
2529
AutoFitFramesX = AutoFitFramesY = -1;
2530
AutoFitOnlyGrows = false;
2531
AutoFitChildAxises = 0x00;
2532
AutoPosLastDirection = ImGuiDir_None;
2533
HiddenFramesRegular = HiddenFramesForResize = 0;
2534
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2535
SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2536
2537
LastFrameActive = -1;
2538
ItemWidthDefault = 0.0f;
2539
FontWindowScale = 1.0f;
2540
SettingsIdx = -1;
2541
2542
DrawList = &DrawListInst;
2543
DrawList->_OwnerName = Name;
2544
ParentWindow = NULL;
2545
RootWindow = NULL;
2546
RootWindowForTitleBarHighlight = NULL;
2547
RootWindowForNav = NULL;
2548
2549
NavLastIds[0] = NavLastIds[1] = 0;
2550
NavRectRel[0] = NavRectRel[1] = ImRect();
2551
NavLastChildNavWindow = NULL;
2552
2553
FocusIdxAllCounter = FocusIdxTabCounter = -1;
2554
FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2555
FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2556
}
2557
2558
ImGuiWindow::~ImGuiWindow()
2559
{
2560
IM_ASSERT(DrawList == &DrawListInst);
2561
IM_DELETE(Name);
2562
for (int i = 0; i != ColumnsStorage.Size; i++)
2563
ColumnsStorage[i].~ImGuiColumnsSet();
2564
}
2565
2566
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2567
{
2568
ImGuiID seed = IDStack.back();
2569
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2570
ImGui::KeepAliveID(id);
2571
return id;
2572
}
2573
2574
ImGuiID ImGuiWindow::GetID(const void* ptr)
2575
{
2576
ImGuiID seed = IDStack.back();
2577
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2578
ImGui::KeepAliveID(id);
2579
return id;
2580
}
2581
2582
ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2583
{
2584
ImGuiID seed = IDStack.back();
2585
return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2586
}
2587
2588
ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2589
{
2590
ImGuiID seed = IDStack.back();
2591
return ImHashData(&ptr, sizeof(void*), seed);
2592
}
2593
2594
// This is only used in rare/specific situations to manufacture an ID out of nowhere.
2595
ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2596
{
2597
ImGuiID seed = IDStack.back();
2598
const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
2599
ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2600
ImGui::KeepAliveID(id);
2601
return id;
2602
}
2603
2604
static void SetCurrentWindow(ImGuiWindow* window)
2605
{
2606
ImGuiContext& g = *GImGui;
2607
g.CurrentWindow = window;
2608
if (window)
2609
g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2610
}
2611
2612
void ImGui::SetNavID(ImGuiID id, int nav_layer)
2613
{
2614
ImGuiContext& g = *GImGui;
2615
IM_ASSERT(g.NavWindow);
2616
IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2617
g.NavId = id;
2618
g.NavWindow->NavLastIds[nav_layer] = id;
2619
}
2620
2621
void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2622
{
2623
ImGuiContext& g = *GImGui;
2624
SetNavID(id, nav_layer);
2625
g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2626
g.NavMousePosDirty = true;
2627
g.NavDisableHighlight = false;
2628
g.NavDisableMouseHover = true;
2629
}
2630
2631
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2632
{
2633
ImGuiContext& g = *GImGui;
2634
g.ActiveIdIsJustActivated = (g.ActiveId != id);
2635
if (g.ActiveIdIsJustActivated)
2636
{
2637
g.ActiveIdTimer = 0.0f;
2638
g.ActiveIdHasBeenPressed = false;
2639
g.ActiveIdHasBeenEdited = false;
2640
if (id != 0)
2641
{
2642
g.LastActiveId = id;
2643
g.LastActiveIdTimer = 0.0f;
2644
}
2645
}
2646
g.ActiveId = id;
2647
g.ActiveIdAllowNavDirFlags = 0;
2648
g.ActiveIdBlockNavInputFlags = 0;
2649
g.ActiveIdAllowOverlap = false;
2650
g.ActiveIdWindow = window;
2651
if (id)
2652
{
2653
g.ActiveIdIsAlive = id;
2654
g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2655
}
2656
}
2657
2658
// FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
2659
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2660
{
2661
ImGuiContext& g = *GImGui;
2662
IM_ASSERT(id != 0);
2663
2664
// Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2665
const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2666
if (g.NavWindow != window)
2667
g.NavInitRequest = false;
2668
g.NavId = id;
2669
g.NavWindow = window;
2670
g.NavLayer = nav_layer;
2671
window->NavLastIds[nav_layer] = id;
2672
if (window->DC.LastItemId == id)
2673
window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2674
2675
if (g.ActiveIdSource == ImGuiInputSource_Nav)
2676
g.NavDisableMouseHover = true;
2677
else
2678
g.NavDisableHighlight = true;
2679
}
2680
2681
void ImGui::ClearActiveID()
2682
{
2683
SetActiveID(0, NULL);
2684
}
2685
2686
void ImGui::SetHoveredID(ImGuiID id)
2687
{
2688
ImGuiContext& g = *GImGui;
2689
g.HoveredId = id;
2690
g.HoveredIdAllowOverlap = false;
2691
if (id != 0 && g.HoveredIdPreviousFrame != id)
2692
g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2693
}
2694
2695
ImGuiID ImGui::GetHoveredID()
2696
{
2697
ImGuiContext& g = *GImGui;
2698
return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2699
}
2700
2701
void ImGui::KeepAliveID(ImGuiID id)
2702
{
2703
ImGuiContext& g = *GImGui;
2704
if (g.ActiveId == id)
2705
g.ActiveIdIsAlive = id;
2706
if (g.ActiveIdPreviousFrame == id)
2707
g.ActiveIdPreviousFrameIsAlive = true;
2708
}
2709
2710
void ImGui::MarkItemEdited(ImGuiID id)
2711
{
2712
// This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2713
// ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data.
2714
ImGuiContext& g = *GImGui;
2715
IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2716
IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2717
//IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2718
g.ActiveIdHasBeenEdited = true;
2719
g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2720
}
2721
2722
static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2723
{
2724
// An active popup disable hovering on other windows (apart from its own children)
2725
// FIXME-OPT: This could be cached/stored within the window.
2726
ImGuiContext& g = *GImGui;
2727
if (g.NavWindow)
2728
if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2729
if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2730
{
2731
// For the purpose of those flags we differentiate "standard popup" from "modal popup"
2732
// NB: The order of those two tests is important because Modal windows are also Popups.
2733
if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2734
return false;
2735
if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2736
return false;
2737
}
2738
2739
return true;
2740
}
2741
2742
// Advance cursor given item size for layout.
2743
void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2744
{
2745
ImGuiContext& g = *GImGui;
2746
ImGuiWindow* window = g.CurrentWindow;
2747
if (window->SkipItems)
2748
return;
2749
2750
// Always align ourselves on pixel boundaries
2751
const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2752
const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2753
//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]
2754
window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2755
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
2756
window->DC.CursorPos.y = (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);
2757
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2758
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2759
//if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2760
2761
window->DC.PrevLineSize.y = line_height;
2762
window->DC.PrevLineTextBaseOffset = text_base_offset;
2763
window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2764
2765
// Horizontal layout mode
2766
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2767
SameLine();
2768
}
2769
2770
void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2771
{
2772
ItemSize(bb.GetSize(), text_offset_y);
2773
}
2774
2775
// Declare item bounding box for clipping and interaction.
2776
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2777
// declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
2778
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2779
{
2780
ImGuiContext& g = *GImGui;
2781
ImGuiWindow* window = g.CurrentWindow;
2782
2783
if (id != 0)
2784
{
2785
// Navigation processing runs prior to clipping early-out
2786
// (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2787
// (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window.
2788
// it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2789
// We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
2790
window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2791
if (g.NavId == id || g.NavAnyRequest)
2792
if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2793
if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2794
NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2795
}
2796
2797
window->DC.LastItemId = id;
2798
window->DC.LastItemRect = bb;
2799
window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2800
2801
#ifdef IMGUI_ENABLE_TEST_ENGINE
2802
if (id != 0)
2803
ImGuiTestEngineHook_ItemAdd(&g, nav_bb_arg ? *nav_bb_arg : bb, id);
2804
#endif
2805
2806
// Clipping test
2807
const bool is_clipped = IsClippedEx(bb, id, false);
2808
if (is_clipped)
2809
return false;
2810
//if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2811
2812
// We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2813
if (IsMouseHoveringRect(bb.Min, bb.Max))
2814
window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2815
return true;
2816
}
2817
2818
// This is roughly matching the behavior of internal-facing ItemHoverable()
2819
// - 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()
2820
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
2821
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2822
{
2823
ImGuiContext& g = *GImGui;
2824
ImGuiWindow* window = g.CurrentWindow;
2825
if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2826
return IsItemFocused();
2827
2828
// Test for bounding box overlap, as updated as ItemAdd()
2829
if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2830
return false;
2831
IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2832
2833
// Test if we are hovering the right window (our window could be behind another window)
2834
// [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 to use IsItemHovered() after EndChild() itself.
2835
// Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
2836
//if (g.HoveredWindow != window)
2837
// return false;
2838
if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2839
return false;
2840
2841
// Test if another item is active (e.g. being dragged)
2842
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2843
if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2844
return false;
2845
2846
// Test if interactions on this window are blocked by an active popup or modal
2847
if (!IsWindowContentHoverable(window, flags))
2848
return false;
2849
2850
// Test if the item is disabled
2851
if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2852
return false;
2853
2854
// Special handling for the dummy item after Begin() which represent the title bar or tab.
2855
// When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2856
if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2857
return false;
2858
return true;
2859
}
2860
2861
// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
2862
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2863
{
2864
ImGuiContext& g = *GImGui;
2865
if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2866
return false;
2867
2868
ImGuiWindow* window = g.CurrentWindow;
2869
if (g.HoveredWindow != window)
2870
return false;
2871
if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2872
return false;
2873
if (!IsMouseHoveringRect(bb.Min, bb.Max))
2874
return false;
2875
if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2876
return false;
2877
if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2878
return false;
2879
2880
SetHoveredID(id);
2881
return true;
2882
}
2883
2884
bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2885
{
2886
ImGuiContext& g = *GImGui;
2887
ImGuiWindow* window = g.CurrentWindow;
2888
if (!bb.Overlaps(window->ClipRect))
2889
if (id == 0 || id != g.ActiveId)
2890
if (clip_even_when_logged || !g.LogEnabled)
2891
return true;
2892
return false;
2893
}
2894
2895
bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2896
{
2897
ImGuiContext& g = *GImGui;
2898
2899
const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2900
window->FocusIdxAllCounter++;
2901
if (is_tab_stop)
2902
window->FocusIdxTabCounter++;
2903
2904
// Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2905
// Note that we can always TAB out of a widget that doesn't allow tabbing in.
2906
if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2907
window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2908
2909
if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2910
return true;
2911
if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2912
{
2913
g.NavJustTabbedId = id;
2914
return true;
2915
}
2916
2917
return false;
2918
}
2919
2920
void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2921
{
2922
window->FocusIdxAllCounter--;
2923
window->FocusIdxTabCounter--;
2924
}
2925
2926
ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2927
{
2928
ImGuiContext& g = *GImGui;
2929
ImVec2 content_max;
2930
if (size.x < 0.0f || size.y < 0.0f)
2931
content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2932
if (size.x <= 0.0f)
2933
size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2934
if (size.y <= 0.0f)
2935
size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2936
return size;
2937
}
2938
2939
float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2940
{
2941
if (wrap_pos_x < 0.0f)
2942
return 0.0f;
2943
2944
ImGuiWindow* window = GetCurrentWindowRead();
2945
if (wrap_pos_x == 0.0f)
2946
wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2947
else if (wrap_pos_x > 0.0f)
2948
wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2949
2950
return ImMax(wrap_pos_x - pos.x, 1.0f);
2951
}
2952
2953
void* ImGui::MemAlloc(size_t size)
2954
{
2955
if (ImGuiContext* ctx = GImGui)
2956
ctx->IO.MetricsActiveAllocations++;
2957
return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2958
}
2959
2960
void ImGui::MemFree(void* ptr)
2961
{
2962
if (ptr)
2963
if (ImGuiContext* ctx = GImGui)
2964
ctx->IO.MetricsActiveAllocations--;
2965
return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2966
}
2967
2968
const char* ImGui::GetClipboardText()
2969
{
2970
return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2971
}
2972
2973
void ImGui::SetClipboardText(const char* text)
2974
{
2975
if (GImGui->IO.SetClipboardTextFn)
2976
GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2977
}
2978
2979
const char* ImGui::GetVersion()
2980
{
2981
return IMGUI_VERSION;
2982
}
2983
2984
// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2985
// 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
2986
ImGuiContext* ImGui::GetCurrentContext()
2987
{
2988
return GImGui;
2989
}
2990
2991
void ImGui::SetCurrentContext(ImGuiContext* ctx)
2992
{
2993
#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2994
IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2995
#else
2996
GImGui = ctx;
2997
#endif
2998
}
2999
3000
// Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3001
// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic.
3002
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)
3003
{
3004
bool error = false;
3005
if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
3006
if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
3007
if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
3008
if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
3009
if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
3010
if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
3011
return !error;
3012
}
3013
3014
void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3015
{
3016
GImAllocatorAllocFunc = alloc_func;
3017
GImAllocatorFreeFunc = free_func;
3018
GImAllocatorUserData = user_data;
3019
}
3020
3021
ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3022
{
3023
ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3024
if (GImGui == NULL)
3025
SetCurrentContext(ctx);
3026
Initialize(ctx);
3027
return ctx;
3028
}
3029
3030
void ImGui::DestroyContext(ImGuiContext* ctx)
3031
{
3032
if (ctx == NULL)
3033
ctx = GImGui;
3034
Shutdown(ctx);
3035
if (GImGui == ctx)
3036
SetCurrentContext(NULL);
3037
IM_DELETE(ctx);
3038
}
3039
3040
ImGuiIO& ImGui::GetIO()
3041
{
3042
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3043
return GImGui->IO;
3044
}
3045
3046
ImGuiStyle& ImGui::GetStyle()
3047
{
3048
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3049
return GImGui->Style;
3050
}
3051
3052
// Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
3053
ImDrawData* ImGui::GetDrawData()
3054
{
3055
ImGuiContext& g = *GImGui;
3056
return g.DrawData.Valid ? &g.DrawData : NULL;
3057
}
3058
3059
double ImGui::GetTime()
3060
{
3061
return GImGui->Time;
3062
}
3063
3064
int ImGui::GetFrameCount()
3065
{
3066
return GImGui->FrameCount;
3067
}
3068
3069
static ImDrawList* GetOverlayDrawList(ImGuiWindow*)
3070
{
3071
// This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches.
3072
return &GImGui->OverlayDrawList;
3073
}
3074
3075
ImDrawList* ImGui::GetOverlayDrawList()
3076
{
3077
return &GImGui->OverlayDrawList;
3078
}
3079
3080
ImDrawListSharedData* ImGui::GetDrawListSharedData()
3081
{
3082
return &GImGui->DrawListSharedData;
3083
}
3084
3085
void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3086
{
3087
// Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3088
// We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3089
// This is because we want ActiveId to be set even when the window is not permitted to move.
3090
ImGuiContext& g = *GImGui;
3091
FocusWindow(window);
3092
SetActiveID(window->MoveId, window);
3093
g.NavDisableHighlight = true;
3094
g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3095
3096
bool can_move_window = true;
3097
if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3098
can_move_window = false;
3099
if (can_move_window)
3100
g.MovingWindow = window;
3101
}
3102
3103
// Handle mouse moving window
3104
// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3105
void ImGui::UpdateMouseMovingWindowNewFrame()
3106
{
3107
ImGuiContext& g = *GImGui;
3108
if (g.MovingWindow != NULL)
3109
{
3110
// We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3111
// We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3112
KeepAliveID(g.ActiveId);
3113
IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3114
ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3115
if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3116
{
3117
ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3118
if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3119
{
3120
MarkIniSettingsDirty(moving_window);
3121
SetWindowPos(moving_window, pos, ImGuiCond_Always);
3122
}
3123
FocusWindow(g.MovingWindow);
3124
}
3125
else
3126
{
3127
ClearActiveID();
3128
g.MovingWindow = NULL;
3129
}
3130
}
3131
else
3132
{
3133
// When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3134
if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3135
{
3136
KeepAliveID(g.ActiveId);
3137
if (!g.IO.MouseDown[0])
3138
ClearActiveID();
3139
}
3140
}
3141
}
3142
3143
// Initiate moving window, handle left-click and right-click focus
3144
void ImGui::UpdateMouseMovingWindowEndFrame()
3145
{
3146
// Initiate moving window
3147
ImGuiContext& g = *GImGui;
3148
if (g.ActiveId != 0 || g.HoveredId != 0)
3149
return;
3150
3151
// Unless we just made a window/popup appear
3152
if (g.NavWindow && g.NavWindow->Appearing)
3153
return;
3154
3155
// Click to focus window and start moving (after we're done with all our widgets)
3156
if (g.IO.MouseClicked[0])
3157
{
3158
if (g.HoveredRootWindow != NULL)
3159
{
3160
StartMouseMovingWindow(g.HoveredWindow);
3161
if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3162
if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3163
g.MovingWindow = NULL;
3164
}
3165
else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3166
{
3167
// Clicking on void disable focus
3168
FocusWindow(NULL);
3169
}
3170
}
3171
3172
// With right mouse button we close popups without changing focus
3173
// (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3174
if (g.IO.MouseClicked[1])
3175
{
3176
// Find the top-most window between HoveredWindow and the front most Modal Window.
3177
// This is where we can trim the popup stack.
3178
ImGuiWindow* modal = GetFrontMostPopupModal();
3179
bool hovered_window_above_modal = false;
3180
if (modal == NULL)
3181
hovered_window_above_modal = true;
3182
for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3183
{
3184
ImGuiWindow* window = g.Windows[i];
3185
if (window == modal)
3186
break;
3187
if (window == g.HoveredWindow)
3188
hovered_window_above_modal = true;
3189
}
3190
ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3191
}
3192
}
3193
3194
static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3195
{
3196
return (window->Active) && (!window->Hidden);
3197
}
3198
3199
static void ImGui::UpdateMouseInputs()
3200
{
3201
ImGuiContext& g = *GImGui;
3202
3203
// Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3204
if (IsMousePosValid(&g.IO.MousePos))
3205
g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3206
3207
// If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3208
if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3209
g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3210
else
3211
g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3212
if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3213
g.NavDisableMouseHover = false;
3214
3215
g.IO.MousePosPrev = g.IO.MousePos;
3216
for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3217
{
3218
g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3219
g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3220
g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3221
g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f;
3222
g.IO.MouseDoubleClicked[i] = false;
3223
if (g.IO.MouseClicked[i])
3224
{
3225
if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3226
{
3227
ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3228
if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3229
g.IO.MouseDoubleClicked[i] = true;
3230
g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
3231
}
3232
else
3233
{
3234
g.IO.MouseClickedTime[i] = g.Time;
3235
}
3236
g.IO.MouseClickedPos[i] = g.IO.MousePos;
3237
g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3238
g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3239
}
3240
else if (g.IO.MouseDown[i])
3241
{
3242
// Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3243
ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3244
g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3245
g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x);
3246
g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y);
3247
}
3248
if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3249
g.NavDisableMouseHover = false;
3250
}
3251
}
3252
3253
void ImGui::UpdateMouseWheel()
3254
{
3255
ImGuiContext& g = *GImGui;
3256
if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3257
return;
3258
if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3259
return;
3260
3261
// If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
3262
ImGuiWindow* window = g.HoveredWindow;
3263
ImGuiWindow* scroll_window = window;
3264
while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow)
3265
scroll_window = scroll_window->ParentWindow;
3266
const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs);
3267
3268
if (g.IO.MouseWheel != 0.0f)
3269
{
3270
if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3271
{
3272
// Zoom / Scale window
3273
const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3274
const float scale = new_font_scale / window->FontWindowScale;
3275
window->FontWindowScale = new_font_scale;
3276
3277
const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3278
window->Pos += offset;
3279
window->Size *= scale;
3280
window->SizeFull *= scale;
3281
}
3282
else if (!g.IO.KeyCtrl && scroll_allowed)
3283
{
3284
// Mouse wheel vertical scrolling
3285
float scroll_amount = 5 * scroll_window->CalcFontSize();
3286
scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3287
SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3288
}
3289
}
3290
if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3291
{
3292
// Mouse wheel horizontal scrolling (for hardware that supports it)
3293
float scroll_amount = scroll_window->CalcFontSize();
3294
SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3295
}
3296
}
3297
3298
// 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)
3299
void ImGui::UpdateHoveredWindowAndCaptureFlags()
3300
{
3301
ImGuiContext& g = *GImGui;
3302
3303
// Find the window hovered by mouse:
3304
// - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3305
// - 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.
3306
// - 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.
3307
FindHoveredWindow();
3308
3309
// Modal windows prevents cursor from hovering behind them.
3310
ImGuiWindow* modal_window = GetFrontMostPopupModal();
3311
if (modal_window)
3312
if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3313
g.HoveredRootWindow = g.HoveredWindow = NULL;
3314
3315
// Disabled mouse?
3316
if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3317
g.HoveredWindow = g.HoveredRootWindow = NULL;
3318
3319
// We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3320
int mouse_earliest_button_down = -1;
3321
bool mouse_any_down = false;
3322
for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3323
{
3324
if (g.IO.MouseClicked[i])
3325
g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3326
mouse_any_down |= g.IO.MouseDown[i];
3327
if (g.IO.MouseDown[i])
3328
if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3329
mouse_earliest_button_down = i;
3330
}
3331
const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3332
3333
// If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3334
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3335
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3336
if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3337
g.HoveredWindow = g.HoveredRootWindow = NULL;
3338
3339
// Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3340
if (g.WantCaptureMouseNextFrame != -1)
3341
g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3342
else
3343
g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3344
3345
// Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3346
if (g.WantCaptureKeyboardNextFrame != -1)
3347
g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3348
else
3349
g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3350
if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3351
g.IO.WantCaptureKeyboard = true;
3352
3353
// Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3354
g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3355
}
3356
3357
void ImGui::NewFrame()
3358
{
3359
IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3360
ImGuiContext& g = *GImGui;
3361
3362
#ifdef IMGUI_ENABLE_TEST_ENGINE
3363
ImGuiTestEngineHook_PreNewFrame(&g);
3364
#endif
3365
3366
// Check user data
3367
// (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)
3368
IM_ASSERT(g.Initialized);
3369
IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
3370
IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
3371
IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3372
IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3373
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
3374
IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!");
3375
IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3376
for (int n = 0; n < ImGuiKey_COUNT; n++)
3377
IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
3378
3379
// Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
3380
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3381
IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3382
3383
// Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3384
if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3385
g.IO.ConfigWindowsResizeFromEdges = false;
3386
3387
// Load settings on first frame (if not explicitly loaded manually before)
3388
if (!g.SettingsLoaded)
3389
{
3390
IM_ASSERT(g.SettingsWindows.empty());
3391
if (g.IO.IniFilename)
3392
LoadIniSettingsFromDisk(g.IO.IniFilename);
3393
g.SettingsLoaded = true;
3394
}
3395
3396
// Save settings (with a delay after the last modification, so we don't spam disk too much)
3397
if (g.SettingsDirtyTimer > 0.0f)
3398
{
3399
g.SettingsDirtyTimer -= g.IO.DeltaTime;
3400
if (g.SettingsDirtyTimer <= 0.0f)
3401
{
3402
if (g.IO.IniFilename != NULL)
3403
SaveIniSettingsToDisk(g.IO.IniFilename);
3404
else
3405
g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3406
g.SettingsDirtyTimer = 0.0f;
3407
}
3408
}
3409
3410
g.Time += g.IO.DeltaTime;
3411
g.FrameScopeActive = true;
3412
g.FrameCount += 1;
3413
g.TooltipOverrideCount = 0;
3414
g.WindowsActiveCount = 0;
3415
3416
// Setup current font and draw list shared data
3417
g.IO.Fonts->Locked = true;
3418
SetCurrentFont(GetDefaultFont());
3419
IM_ASSERT(g.Font->IsLoaded());
3420
g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3421
g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3422
3423
g.OverlayDrawList.Clear();
3424
g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3425
g.OverlayDrawList.PushClipRectFullScreen();
3426
g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3427
3428
// Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3429
g.DrawData.Clear();
3430
3431
// Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3432
if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3433
KeepAliveID(g.DragDropPayload.SourceId);
3434
3435
// Clear reference to active widget if the widget isn't alive anymore
3436
if (!g.HoveredIdPreviousFrame)
3437
g.HoveredIdTimer = 0.0f;
3438
if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3439
g.HoveredIdNotActiveTimer = 0.0f;
3440
if (g.HoveredId)
3441
g.HoveredIdTimer += g.IO.DeltaTime;
3442
if (g.HoveredId && g.ActiveId != g.HoveredId)
3443
g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3444
g.HoveredIdPreviousFrame = g.HoveredId;
3445
g.HoveredId = 0;
3446
g.HoveredIdAllowOverlap = false;
3447
if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3448
ClearActiveID();
3449
if (g.ActiveId)
3450
g.ActiveIdTimer += g.IO.DeltaTime;
3451
g.LastActiveIdTimer += g.IO.DeltaTime;
3452
g.ActiveIdPreviousFrame = g.ActiveId;
3453
g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3454
g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3455
g.ActiveIdIsAlive = 0;
3456
g.ActiveIdPreviousFrameIsAlive = false;
3457
g.ActiveIdIsJustActivated = false;
3458
if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3459
g.ScalarAsInputTextId = 0;
3460
3461
// Drag and drop
3462
g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3463
g.DragDropAcceptIdCurr = 0;
3464
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3465
g.DragDropWithinSourceOrTarget = false;
3466
3467
// Update keyboard input state
3468
memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3469
for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3470
g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
3471
3472
// Update gamepad/keyboard directional navigation
3473
NavUpdate();
3474
3475
// Update mouse input state
3476
UpdateMouseInputs();
3477
3478
// Calculate frame-rate for the user, as a purely luxurious feature
3479
g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3480
g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3481
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3482
g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3483
3484
// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3485
UpdateMouseMovingWindowNewFrame();
3486
UpdateHoveredWindowAndCaptureFlags();
3487
3488
// Background darkening/whitening
3489
if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3490
g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3491
else
3492
g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3493
3494
g.MouseCursor = ImGuiMouseCursor_Arrow;
3495
g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3496
g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3497
3498
// Mouse wheel scrolling, scale
3499
UpdateMouseWheel();
3500
3501
// Pressing TAB activate widget focus
3502
if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3503
{
3504
if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3505
g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3506
else
3507
g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3508
}
3509
g.NavIdTabCounter = INT_MAX;
3510
3511
// Mark all windows as not visible
3512
IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3513
for (int i = 0; i != g.Windows.Size; i++)
3514
{
3515
ImGuiWindow* window = g.Windows[i];
3516
window->WasActive = window->Active;
3517
window->Active = false;
3518
window->WriteAccessed = false;
3519
}
3520
3521
// Closing the focused window restore focus to the first active root window in descending z-order
3522
if (g.NavWindow && !g.NavWindow->WasActive)
3523
FocusPreviousWindowIgnoringOne(NULL);
3524
3525
// No window should be open at the beginning of the frame.
3526
// But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3527
g.CurrentWindowStack.resize(0);
3528
g.BeginPopupStack.resize(0);
3529
ClosePopupsOverWindow(g.NavWindow);
3530
3531
// Create implicit/fallback window - which we will only render it if the user has added something to it.
3532
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3533
// This fallback is particularly important as it avoid ImGui:: calls from crashing.
3534
SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3535
Begin("Debug##Default");
3536
g.FrameScopePushedImplicitWindow = true;
3537
3538
#ifdef IMGUI_ENABLE_TEST_ENGINE
3539
ImGuiTestEngineHook_PostNewFrame(&g);
3540
#endif
3541
}
3542
3543
void ImGui::Initialize(ImGuiContext* context)
3544
{
3545
ImGuiContext& g = *context;
3546
IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3547
3548
// Add .ini handle for ImGuiWindow type
3549
ImGuiSettingsHandler ini_handler;
3550
ini_handler.TypeName = "Window";
3551
ini_handler.TypeHash = ImHashStr("Window", 0);
3552
ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3553
ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3554
ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3555
g.SettingsHandlers.push_back(ini_handler);
3556
3557
g.Initialized = true;
3558
}
3559
3560
// This function is merely here to free heap allocations.
3561
void ImGui::Shutdown(ImGuiContext* context)
3562
{
3563
// 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)
3564
ImGuiContext& g = *context;
3565
if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3566
{
3567
g.IO.Fonts->Locked = false;
3568
IM_DELETE(g.IO.Fonts);
3569
}
3570
g.IO.Fonts = NULL;
3571
3572
// Cleanup of other data are conditional on actually having initialized ImGui.
3573
if (!g.Initialized)
3574
return;
3575
3576
// Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3577
if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3578
{
3579
ImGuiContext* backup_context = GImGui;
3580
SetCurrentContext(context);
3581
SaveIniSettingsToDisk(g.IO.IniFilename);
3582
SetCurrentContext(backup_context);
3583
}
3584
3585
// Clear everything else
3586
for (int i = 0; i < g.Windows.Size; i++)
3587
IM_DELETE(g.Windows[i]);
3588
g.Windows.clear();
3589
g.WindowsFocusOrder.clear();
3590
g.WindowsSortBuffer.clear();
3591
g.CurrentWindow = NULL;
3592
g.CurrentWindowStack.clear();
3593
g.WindowsById.Clear();
3594
g.NavWindow = NULL;
3595
g.HoveredWindow = g.HoveredRootWindow = NULL;
3596
g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3597
g.MovingWindow = NULL;
3598
g.ColorModifiers.clear();
3599
g.StyleModifiers.clear();
3600
g.FontStack.clear();
3601
g.OpenPopupStack.clear();
3602
g.BeginPopupStack.clear();
3603
g.DrawDataBuilder.ClearFreeMemory();
3604
g.OverlayDrawList.ClearFreeMemory();
3605
g.PrivateClipboard.clear();
3606
g.InputTextState.TextW.clear();
3607
g.InputTextState.InitialText.clear();
3608
g.InputTextState.TempBuffer.clear();
3609
3610
for (int i = 0; i < g.SettingsWindows.Size; i++)
3611
IM_DELETE(g.SettingsWindows[i].Name);
3612
g.SettingsWindows.clear();
3613
g.SettingsHandlers.clear();
3614
3615
if (g.LogFile && g.LogFile != stdout)
3616
{
3617
fclose(g.LogFile);
3618
g.LogFile = NULL;
3619
}
3620
g.LogClipboard.clear();
3621
3622
g.Initialized = false;
3623
}
3624
3625
// FIXME: Add a more explicit sort order in the window structure.
3626
static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3627
{
3628
const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3629
const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3630
if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3631
return d;
3632
if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3633
return d;
3634
return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3635
}
3636
3637
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3638
{
3639
out_sorted_windows->push_back(window);
3640
if (window->Active)
3641
{
3642
int count = window->DC.ChildWindows.Size;
3643
if (count > 1)
3644
ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3645
for (int i = 0; i < count; i++)
3646
{
3647
ImGuiWindow* child = window->DC.ChildWindows[i];
3648
if (child->Active)
3649
AddWindowToSortBuffer(out_sorted_windows, child);
3650
}
3651
}
3652
}
3653
3654
static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3655
{
3656
if (draw_list->CmdBuffer.empty())
3657
return;
3658
3659
// Remove trailing command if unused
3660
ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3661
if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3662
{
3663
draw_list->CmdBuffer.pop_back();
3664
if (draw_list->CmdBuffer.empty())
3665
return;
3666
}
3667
3668
// Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
3669
IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3670
IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3671
IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3672
3673
// Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3674
// If this assert triggers because you are drawing lots of stuff manually:
3675
// A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents.
3676
// B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
3677
// You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
3678
// glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3679
// Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
3680
// C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
3681
if (sizeof(ImDrawIdx) == 2)
3682
IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3683
3684
out_list->push_back(draw_list);
3685
}
3686
3687
static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3688
{
3689
ImGuiContext& g = *GImGui;
3690
g.IO.MetricsRenderWindows++;
3691
AddDrawListToDrawData(out_render_list, window->DrawList);
3692
for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3693
{
3694
ImGuiWindow* child = window->DC.ChildWindows[i];
3695
if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3696
AddWindowToDrawData(out_render_list, child);
3697
}
3698
}
3699
3700
static void AddRootWindowToDrawData(ImGuiWindow* window)
3701
{
3702
ImGuiContext& g = *GImGui;
3703
if (window->Flags & ImGuiWindowFlags_Tooltip)
3704
AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3705
else
3706
AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3707
}
3708
3709
void ImDrawDataBuilder::FlattenIntoSingleLayer()
3710
{
3711
int n = Layers[0].Size;
3712
int size = n;
3713
for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3714
size += Layers[i].Size;
3715
Layers[0].resize(size);
3716
for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3717
{
3718
ImVector<ImDrawList*>& layer = Layers[layer_n];
3719
if (layer.empty())
3720
continue;
3721
memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3722
n += layer.Size;
3723
layer.resize(0);
3724
}
3725
}
3726
3727
static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3728
{
3729
ImGuiIO& io = ImGui::GetIO();
3730
draw_data->Valid = true;
3731
draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3732
draw_data->CmdListsCount = draw_lists->Size;
3733
draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3734
draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3735
draw_data->DisplaySize = io.DisplaySize;
3736
draw_data->FramebufferScale = io.DisplayFramebufferScale;
3737
for (int n = 0; n < draw_lists->Size; n++)
3738
{
3739
draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3740
draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3741
}
3742
}
3743
3744
// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
3745
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3746
{
3747
ImGuiWindow* window = GetCurrentWindow();
3748
window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3749
window->ClipRect = window->DrawList->_ClipRectStack.back();
3750
}
3751
3752
void ImGui::PopClipRect()
3753
{
3754
ImGuiWindow* window = GetCurrentWindow();
3755
window->DrawList->PopClipRect();
3756
window->ClipRect = window->DrawList->_ClipRectStack.back();
3757
}
3758
3759
// 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.
3760
void ImGui::EndFrame()
3761
{
3762
ImGuiContext& g = *GImGui;
3763
IM_ASSERT(g.Initialized);
3764
if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
3765
return;
3766
IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?");
3767
3768
// Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3769
if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
3770
{
3771
g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3772
g.PlatformImeLastPos = g.PlatformImePos;
3773
}
3774
3775
// Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
3776
// to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
3777
if (g.CurrentWindowStack.Size != 1)
3778
{
3779
if (g.CurrentWindowStack.Size > 1)
3780
{
3781
IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
3782
while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING
3783
End();
3784
}
3785
else
3786
{
3787
IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
3788
}
3789
}
3790
3791
// Hide implicit/fallback "Debug" window if it hasn't been used
3792
g.FrameScopePushedImplicitWindow = false;
3793
if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3794
g.CurrentWindow->Active = false;
3795
End();
3796
3797
// Show CTRL+TAB list window
3798
if (g.NavWindowingTarget)
3799
NavUpdateWindowingList();
3800
3801
// Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3802
if (g.DragDropActive)
3803
{
3804
bool is_delivered = g.DragDropPayload.Delivery;
3805
bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3806
if (is_delivered || is_elapsed)
3807
ClearDragDrop();
3808
}
3809
3810
// Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3811
if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3812
{
3813
g.DragDropWithinSourceOrTarget = true;
3814
SetTooltip("...");
3815
g.DragDropWithinSourceOrTarget = false;
3816
}
3817
3818
// End frame
3819
g.FrameScopeActive = false;
3820
g.FrameCountEnded = g.FrameCount;
3821
3822
// Initiate moving window + handle left-click and right-click focus
3823
UpdateMouseMovingWindowEndFrame();
3824
3825
// Sort the window list so that all child windows are after their parent
3826
// We cannot do that on FocusWindow() because childs may not exist yet
3827
g.WindowsSortBuffer.resize(0);
3828
g.WindowsSortBuffer.reserve(g.Windows.Size);
3829
for (int i = 0; i != g.Windows.Size; i++)
3830
{
3831
ImGuiWindow* window = g.Windows[i];
3832
if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
3833
continue;
3834
AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
3835
}
3836
3837
// 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.
3838
IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);
3839
g.Windows.swap(g.WindowsSortBuffer);
3840
g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3841
3842
// Unlock font atlas
3843
g.IO.Fonts->Locked = false;
3844
3845
// Clear Input data for next frame
3846
g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3847
g.IO.InputQueueCharacters.resize(0);
3848
memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3849
}
3850
3851
void ImGui::Render()
3852
{
3853
ImGuiContext& g = *GImGui;
3854
IM_ASSERT(g.Initialized);
3855
3856
if (g.FrameCountEnded != g.FrameCount)
3857
EndFrame();
3858
g.FrameCountRendered = g.FrameCount;
3859
3860
// Gather ImDrawList to render (for each active window)
3861
g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3862
g.DrawDataBuilder.Clear();
3863
ImGuiWindow* windows_to_render_front_most[2];
3864
windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3865
windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3866
for (int n = 0; n != g.Windows.Size; n++)
3867
{
3868
ImGuiWindow* window = g.Windows[n];
3869
if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3870
AddRootWindowToDrawData(window);
3871
}
3872
for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3873
if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3874
AddRootWindowToDrawData(windows_to_render_front_most[n]);
3875
g.DrawDataBuilder.FlattenIntoSingleLayer();
3876
3877
// Draw software mouse cursor if requested
3878
if (g.IO.MouseDrawCursor)
3879
RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3880
3881
if (!g.OverlayDrawList.VtxBuffer.empty())
3882
AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3883
3884
// Setup ImDrawData structure for end-user
3885
SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3886
g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3887
g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3888
3889
// (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
3890
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3891
if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3892
g.IO.RenderDrawListsFn(&g.DrawData);
3893
#endif
3894
}
3895
3896
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3897
// CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
3898
ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3899
{
3900
ImGuiContext& g = *GImGui;
3901
3902
const char* text_display_end;
3903
if (hide_text_after_double_hash)
3904
text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
3905
else
3906
text_display_end = text_end;
3907
3908
ImFont* font = g.Font;
3909
const float font_size = g.FontSize;
3910
if (text == text_display_end)
3911
return ImVec2(0.0f, font_size);
3912
ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3913
3914
// Round
3915
text_size.x = (float)(int)(text_size.x + 0.95f);
3916
3917
return text_size;
3918
}
3919
3920
// Helper to calculate coarse clipping of large list of evenly sized items.
3921
// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3922
// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
3923
void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3924
{
3925
ImGuiContext& g = *GImGui;
3926
ImGuiWindow* window = g.CurrentWindow;
3927
if (g.LogEnabled)
3928
{
3929
// If logging is active, do not perform any clipping
3930
*out_items_display_start = 0;
3931
*out_items_display_end = items_count;
3932
return;
3933
}
3934
if (window->SkipItems)
3935
{
3936
*out_items_display_start = *out_items_display_end = 0;
3937
return;
3938
}
3939
3940
// We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3941
ImRect unclipped_rect = window->ClipRect;
3942
if (g.NavMoveRequest)
3943
unclipped_rect.Add(g.NavScoringRectScreen);
3944
3945
const ImVec2 pos = window->DC.CursorPos;
3946
int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3947
int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3948
3949
// When performing a navigation request, ensure we have one item extra in the direction we are moving to
3950
if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3951
start--;
3952
if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3953
end++;
3954
3955
start = ImClamp(start, 0, items_count);
3956
end = ImClamp(end + 1, start, items_count);
3957
*out_items_display_start = start;
3958
*out_items_display_end = end;
3959
}
3960
3961
// Find window given position, search front-to-back
3962
// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3963
// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3964
// called, aka before the next Begin(). Moving window isn't affected.
3965
static void FindHoveredWindow()
3966
{
3967
ImGuiContext& g = *GImGui;
3968
3969
ImGuiWindow* hovered_window = NULL;
3970
if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
3971
hovered_window = g.MovingWindow;
3972
3973
ImVec2 padding_regular = g.Style.TouchExtraPadding;
3974
ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
3975
for (int i = g.Windows.Size - 1; i >= 0; i--)
3976
{
3977
ImGuiWindow* window = g.Windows[i];
3978
if (!window->Active || window->Hidden)
3979
continue;
3980
if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
3981
continue;
3982
3983
// Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3984
ImRect bb(window->OuterRectClipped);
3985
if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize))
3986
bb.Expand(padding_regular);
3987
else
3988
bb.Expand(padding_for_resize_from_edges);
3989
if (!bb.Contains(g.IO.MousePos))
3990
continue;
3991
3992
// Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
3993
if (hovered_window == NULL)
3994
hovered_window = window;
3995
if (hovered_window)
3996
break;
3997
}
3998
3999
g.HoveredWindow = hovered_window;
4000
g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4001
4002
}
4003
4004
// Test if mouse cursor is hovering given rectangle
4005
// NB- Rectangle is clipped by our current clip setting
4006
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
4007
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4008
{
4009
ImGuiContext& g = *GImGui;
4010
4011
// Clip
4012
ImRect rect_clipped(r_min, r_max);
4013
if (clip)
4014
rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4015
4016
// Expand for touch input
4017
const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4018
if (!rect_for_touch.Contains(g.IO.MousePos))
4019
return false;
4020
return true;
4021
}
4022
4023
int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4024
{
4025
IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4026
return GImGui->IO.KeyMap[imgui_key];
4027
}
4028
4029
// Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
4030
bool ImGui::IsKeyDown(int user_key_index)
4031
{
4032
if (user_key_index < 0) return false;
4033
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
4034
return GImGui->IO.KeysDown[user_key_index];
4035
}
4036
4037
int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
4038
{
4039
if (t == 0.0f)
4040
return 1;
4041
if (t <= repeat_delay || repeat_rate <= 0.0f)
4042
return 0;
4043
const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
4044
return (count > 0) ? count : 0;
4045
}
4046
4047
int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4048
{
4049
ImGuiContext& g = *GImGui;
4050
if (key_index < 0)
4051
return 0;
4052
IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4053
const float t = g.IO.KeysDownDuration[key_index];
4054
return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
4055
}
4056
4057
bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4058
{
4059
ImGuiContext& g = *GImGui;
4060
if (user_key_index < 0)
4061
return false;
4062
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4063
const float t = g.IO.KeysDownDuration[user_key_index];
4064
if (t == 0.0f)
4065
return true;
4066
if (repeat && t > g.IO.KeyRepeatDelay)
4067
return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4068
return false;
4069
}
4070
4071
bool ImGui::IsKeyReleased(int user_key_index)
4072
{
4073
ImGuiContext& g = *GImGui;
4074
if (user_key_index < 0) return false;
4075
IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4076
return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4077
}
4078
4079
bool ImGui::IsMouseDown(int button)
4080
{
4081
ImGuiContext& g = *GImGui;
4082
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4083
return g.IO.MouseDown[button];
4084
}
4085
4086
bool ImGui::IsAnyMouseDown()
4087
{
4088
ImGuiContext& g = *GImGui;
4089
for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4090
if (g.IO.MouseDown[n])
4091
return true;
4092
return false;
4093
}
4094
4095
bool ImGui::IsMouseClicked(int button, bool repeat)
4096
{
4097
ImGuiContext& g = *GImGui;
4098
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4099
const float t = g.IO.MouseDownDuration[button];
4100
if (t == 0.0f)
4101
return true;
4102
4103
if (repeat && t > g.IO.KeyRepeatDelay)
4104
{
4105
float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
4106
if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
4107
return true;
4108
}
4109
4110
return false;
4111
}
4112
4113
bool ImGui::IsMouseReleased(int button)
4114
{
4115
ImGuiContext& g = *GImGui;
4116
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4117
return g.IO.MouseReleased[button];
4118
}
4119
4120
bool ImGui::IsMouseDoubleClicked(int button)
4121
{
4122
ImGuiContext& g = *GImGui;
4123
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4124
return g.IO.MouseDoubleClicked[button];
4125
}
4126
4127
bool ImGui::IsMouseDragging(int button, float lock_threshold)
4128
{
4129
ImGuiContext& g = *GImGui;
4130
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4131
if (!g.IO.MouseDown[button])
4132
return false;
4133
if (lock_threshold < 0.0f)
4134
lock_threshold = g.IO.MouseDragThreshold;
4135
return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4136
}
4137
4138
ImVec2 ImGui::GetMousePos()
4139
{
4140
return GImGui->IO.MousePos;
4141
}
4142
4143
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
4144
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4145
{
4146
ImGuiContext& g = *GImGui;
4147
if (g.BeginPopupStack.Size > 0)
4148
return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4149
return g.IO.MousePos;
4150
}
4151
4152
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
4153
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4154
{
4155
// The assert is only to silence a false-positive in XCode Static Analysis.
4156
// 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).
4157
IM_ASSERT(GImGui != NULL);
4158
const float MOUSE_INVALID = -256000.0f;
4159
ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4160
return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4161
}
4162
4163
// Return the delta from the initial clicking position.
4164
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4165
// NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
4166
ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4167
{
4168
ImGuiContext& g = *GImGui;
4169
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4170
if (lock_threshold < 0.0f)
4171
lock_threshold = g.IO.MouseDragThreshold;
4172
if (g.IO.MouseDown[button])
4173
if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4174
return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
4175
return ImVec2(0.0f, 0.0f);
4176
}
4177
4178
void ImGui::ResetMouseDragDelta(int button)
4179
{
4180
ImGuiContext& g = *GImGui;
4181
IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4182
// NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4183
g.IO.MouseClickedPos[button] = g.IO.MousePos;
4184
}
4185
4186
ImGuiMouseCursor ImGui::GetMouseCursor()
4187
{
4188
return GImGui->MouseCursor;
4189
}
4190
4191
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4192
{
4193
GImGui->MouseCursor = cursor_type;
4194
}
4195
4196
void ImGui::CaptureKeyboardFromApp(bool capture)
4197
{
4198
GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4199
}
4200
4201
void ImGui::CaptureMouseFromApp(bool capture)
4202
{
4203
GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4204
}
4205
4206
bool ImGui::IsItemActive()
4207
{
4208
ImGuiContext& g = *GImGui;
4209
if (g.ActiveId)
4210
{
4211
ImGuiWindow* window = g.CurrentWindow;
4212
return g.ActiveId == window->DC.LastItemId;
4213
}
4214
return false;
4215
}
4216
4217
bool ImGui::IsItemActivated()
4218
{
4219
ImGuiContext& g = *GImGui;
4220
if (g.ActiveId)
4221
{
4222
ImGuiWindow* window = g.CurrentWindow;
4223
if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4224
return true;
4225
}
4226
return false;
4227
}
4228
4229
bool ImGui::IsItemDeactivated()
4230
{
4231
ImGuiContext& g = *GImGui;
4232
ImGuiWindow* window = g.CurrentWindow;
4233
return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4234
}
4235
4236
bool ImGui::IsItemDeactivatedAfterEdit()
4237
{
4238
ImGuiContext& g = *GImGui;
4239
return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
4240
}
4241
4242
bool ImGui::IsItemFocused()
4243
{
4244
ImGuiContext& g = *GImGui;
4245
ImGuiWindow* window = g.CurrentWindow;
4246
4247
if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4248
return false;
4249
return true;
4250
}
4251
4252
bool ImGui::IsItemClicked(int mouse_button)
4253
{
4254
return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4255
}
4256
4257
bool ImGui::IsAnyItemHovered()
4258
{
4259
ImGuiContext& g = *GImGui;
4260
return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4261
}
4262
4263
bool ImGui::IsAnyItemActive()
4264
{
4265
ImGuiContext& g = *GImGui;
4266
return g.ActiveId != 0;
4267
}
4268
4269
bool ImGui::IsAnyItemFocused()
4270
{
4271
ImGuiContext& g = *GImGui;
4272
return g.NavId != 0 && !g.NavDisableHighlight;
4273
}
4274
4275
bool ImGui::IsItemVisible()
4276
{
4277
ImGuiWindow* window = GetCurrentWindowRead();
4278
return window->ClipRect.Overlaps(window->DC.LastItemRect);
4279
}
4280
4281
bool ImGui::IsItemEdited()
4282
{
4283
ImGuiWindow* window = GetCurrentWindowRead();
4284
return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4285
}
4286
4287
// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4288
void ImGui::SetItemAllowOverlap()
4289
{
4290
ImGuiContext& g = *GImGui;
4291
if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4292
g.HoveredIdAllowOverlap = true;
4293
if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4294
g.ActiveIdAllowOverlap = true;
4295
}
4296
4297
ImVec2 ImGui::GetItemRectMin()
4298
{
4299
ImGuiWindow* window = GetCurrentWindowRead();
4300
return window->DC.LastItemRect.Min;
4301
}
4302
4303
ImVec2 ImGui::GetItemRectMax()
4304
{
4305
ImGuiWindow* window = GetCurrentWindowRead();
4306
return window->DC.LastItemRect.Max;
4307
}
4308
4309
ImVec2 ImGui::GetItemRectSize()
4310
{
4311
ImGuiWindow* window = GetCurrentWindowRead();
4312
return window->DC.LastItemRect.GetSize();
4313
}
4314
4315
static ImRect GetViewportRect()
4316
{
4317
ImGuiContext& g = *GImGui;
4318
return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4319
}
4320
4321
static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4322
{
4323
ImGuiContext& g = *GImGui;
4324
ImGuiWindow* parent_window = g.CurrentWindow;
4325
4326
flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4327
flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4328
4329
// Size
4330
const ImVec2 content_avail = GetContentRegionAvail();
4331
ImVec2 size = ImFloor(size_arg);
4332
const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4333
if (size.x <= 0.0f)
4334
size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4335
if (size.y <= 0.0f)
4336
size.y = ImMax(content_avail.y + size.y, 4.0f);
4337
SetNextWindowSize(size);
4338
4339
// 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.
4340
char title[256];
4341
if (name)
4342
ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4343
else
4344
ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4345
4346
const float backup_border_size = g.Style.ChildBorderSize;
4347
if (!border)
4348
g.Style.ChildBorderSize = 0.0f;
4349
bool ret = Begin(title, NULL, flags);
4350
g.Style.ChildBorderSize = backup_border_size;
4351
4352
ImGuiWindow* child_window = g.CurrentWindow;
4353
child_window->ChildId = id;
4354
child_window->AutoFitChildAxises = auto_fit_axises;
4355
4356
// Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4357
// While this is not really documented/defined, it seems that the expected thing to do.
4358
if (child_window->BeginCount == 1)
4359
parent_window->DC.CursorPos = child_window->Pos;
4360
4361
// Process navigation-in immediately so NavInit can run on first frame
4362
if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4363
{
4364
FocusWindow(child_window);
4365
NavInitWindow(child_window, false);
4366
SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4367
g.ActiveIdSource = ImGuiInputSource_Nav;
4368
}
4369
return ret;
4370
}
4371
4372
bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4373
{
4374
ImGuiWindow* window = GetCurrentWindow();
4375
return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4376
}
4377
4378
bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4379
{
4380
IM_ASSERT(id != 0);
4381
return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4382
}
4383
4384
void ImGui::EndChild()
4385
{
4386
ImGuiContext& g = *GImGui;
4387
ImGuiWindow* window = g.CurrentWindow;
4388
4389
IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
4390
if (window->BeginCount > 1)
4391
{
4392
End();
4393
}
4394
else
4395
{
4396
ImVec2 sz = window->Size;
4397
if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4398
sz.x = ImMax(4.0f, sz.x);
4399
if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4400
sz.y = ImMax(4.0f, sz.y);
4401
End();
4402
4403
ImGuiWindow* parent_window = g.CurrentWindow;
4404
ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4405
ItemSize(sz);
4406
if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4407
{
4408
ItemAdd(bb, window->ChildId);
4409
RenderNavHighlight(bb, window->ChildId);
4410
4411
// When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4412
if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4413
RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4414
}
4415
else
4416
{
4417
// Not navigable into
4418
ItemAdd(bb, 0);
4419
}
4420
}
4421
}
4422
4423
// Helper to create a child window / scrolling region that looks like a normal widget frame.
4424
bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4425
{
4426
ImGuiContext& g = *GImGui;
4427
const ImGuiStyle& style = g.Style;
4428
PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4429
PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4430
PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4431
PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4432
bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4433
PopStyleVar(3);
4434
PopStyleColor();
4435
return ret;
4436
}
4437
4438
void ImGui::EndChildFrame()
4439
{
4440
EndChild();
4441
}
4442
4443
// Save and compare stack sizes on Begin()/End() to detect usage errors
4444
static void CheckStacksSize(ImGuiWindow* window, bool write)
4445
{
4446
// NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
4447
ImGuiContext& g = *GImGui;
4448
short* p_backup = &window->DC.StackSizesBackup[0];
4449
{ int current = window->IDStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
4450
{ int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
4451
{ int current = g.BeginPopupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4452
// For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.
4453
{ int current = g.ColorModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
4454
{ int current = g.StyleModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
4455
{ int current = g.FontStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
4456
IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4457
}
4458
4459
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4460
{
4461
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4462
window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4463
window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4464
}
4465
4466
ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4467
{
4468
ImGuiContext& g = *GImGui;
4469
return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4470
}
4471
4472
ImGuiWindow* ImGui::FindWindowByName(const char* name)
4473
{
4474
ImGuiID id = ImHashStr(name, 0);
4475
return FindWindowByID(id);
4476
}
4477
4478
static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4479
{
4480
ImGuiContext& g = *GImGui;
4481
4482
// Create window the first time
4483
ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4484
window->Flags = flags;
4485
g.WindowsById.SetVoidPtr(window->ID, window);
4486
4487
// Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4488
window->Pos = ImVec2(60, 60);
4489
4490
// User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4491
if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4492
if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4493
{
4494
// Retrieve settings from .ini file
4495
window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
4496
SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4497
window->Pos = ImFloor(settings->Pos);
4498
window->Collapsed = settings->Collapsed;
4499
if (ImLengthSqr(settings->Size) > 0.00001f)
4500
size = ImFloor(settings->Size);
4501
}
4502
window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(size);
4503
window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4504
4505
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4506
{
4507
window->AutoFitFramesX = window->AutoFitFramesY = 2;
4508
window->AutoFitOnlyGrows = false;
4509
}
4510
else
4511
{
4512
if (window->Size.x <= 0.0f)
4513
window->AutoFitFramesX = 2;
4514
if (window->Size.y <= 0.0f)
4515
window->AutoFitFramesY = 2;
4516
window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4517
}
4518
4519
g.WindowsFocusOrder.push_back(window);
4520
if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4521
g.Windows.push_front(window); // Quite slow but rare and only once
4522
else
4523
g.Windows.push_back(window);
4524
return window;
4525
}
4526
4527
static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4528
{
4529
ImGuiContext& g = *GImGui;
4530
if (g.NextWindowData.SizeConstraintCond != 0)
4531
{
4532
// Using -1,-1 on either X/Y axis to preserve the current size.
4533
ImRect cr = g.NextWindowData.SizeConstraintRect;
4534
new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4535
new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4536
if (g.NextWindowData.SizeCallback)
4537
{
4538
ImGuiSizeCallbackData data;
4539
data.UserData = g.NextWindowData.SizeCallbackUserData;
4540
data.Pos = window->Pos;
4541
data.CurrentSize = window->SizeFull;
4542
data.DesiredSize = new_size;
4543
g.NextWindowData.SizeCallback(&data);
4544
new_size = data.DesiredSize;
4545
}
4546
}
4547
4548
// Minimum size
4549
if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4550
{
4551
new_size = ImMax(new_size, g.Style.WindowMinSize);
4552
new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4553
}
4554
return new_size;
4555
}
4556
4557
static ImVec2 CalcSizeContents(ImGuiWindow* window)
4558
{
4559
if (window->Collapsed)
4560
if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4561
return window->SizeContents;
4562
if (window->Hidden && window->HiddenFramesForResize == 0 && window->HiddenFramesRegular > 0)
4563
return window->SizeContents;
4564
4565
ImVec2 sz;
4566
sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4567
sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4568
return sz + window->WindowPadding;
4569
}
4570
4571
static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4572
{
4573
ImGuiContext& g = *GImGui;
4574
ImGuiStyle& style = g.Style;
4575
if (window->Flags & ImGuiWindowFlags_Tooltip)
4576
{
4577
// Tooltip always resize
4578
return size_contents;
4579
}
4580
else
4581
{
4582
// Maximum window size is determined by the display size
4583
const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4584
const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4585
ImVec2 size_min = style.WindowMinSize;
4586
if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
4587
size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4588
ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4589
4590
// When the window cannot fit all contents (either because of constraints, either because screen is too small),
4591
// we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4592
ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4593
if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4594
size_auto_fit.y += style.ScrollbarSize;
4595
if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4596
size_auto_fit.x += style.ScrollbarSize;
4597
return size_auto_fit;
4598
}
4599
}
4600
4601
ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4602
{
4603
ImVec2 size_contents = CalcSizeContents(window);
4604
return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4605
}
4606
4607
float ImGui::GetWindowScrollMaxX(ImGuiWindow* window)
4608
{
4609
return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4610
}
4611
4612
float ImGui::GetWindowScrollMaxY(ImGuiWindow* window)
4613
{
4614
return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4615
}
4616
4617
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4618
{
4619
ImGuiContext& g = *GImGui;
4620
ImVec2 scroll = window->Scroll;
4621
if (window->ScrollTarget.x < FLT_MAX)
4622
{
4623
float cr_x = window->ScrollTargetCenterRatio.x;
4624
scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4625
}
4626
if (window->ScrollTarget.y < FLT_MAX)
4627
{
4628
// 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
4629
float cr_y = window->ScrollTargetCenterRatio.y;
4630
float target_y = window->ScrollTarget.y;
4631
if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4632
target_y = 0.0f;
4633
if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4634
target_y = window->SizeContents.y;
4635
scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4636
}
4637
scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4638
if (!window->Collapsed && !window->SkipItems)
4639
{
4640
scroll.x = ImMin(scroll.x, ImGui::GetWindowScrollMaxX(window));
4641
scroll.y = ImMin(scroll.y, ImGui::GetWindowScrollMaxY(window));
4642
}
4643
return scroll;
4644
}
4645
4646
static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4647
{
4648
if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4649
return ImGuiCol_PopupBg;
4650
if (flags & ImGuiWindowFlags_ChildWindow)
4651
return ImGuiCol_ChildBg;
4652
return ImGuiCol_WindowBg;
4653
}
4654
4655
static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4656
{
4657
ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
4658
ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4659
ImVec2 size_expected = pos_max - pos_min;
4660
ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4661
*out_pos = pos_min;
4662
if (corner_norm.x == 0.0f)
4663
out_pos->x -= (size_constrained.x - size_expected.x);
4664
if (corner_norm.y == 0.0f)
4665
out_pos->y -= (size_constrained.y - size_expected.y);
4666
*out_size = size_constrained;
4667
}
4668
4669
struct ImGuiResizeGripDef
4670
{
4671
ImVec2 CornerPosN;
4672
ImVec2 InnerDir;
4673
int AngleMin12, AngleMax12;
4674
};
4675
4676
static const ImGuiResizeGripDef resize_grip_def[4] =
4677
{
4678
{ ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4679
{ ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4680
{ ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4681
{ ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4682
};
4683
4684
static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4685
{
4686
ImRect rect = window->Rect();
4687
if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4688
if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top
4689
if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right
4690
if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom
4691
if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left
4692
IM_ASSERT(0);
4693
return ImRect();
4694
}
4695
4696
// Handle resize for: Resize Grips, Borders, Gamepad
4697
static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4698
{
4699
ImGuiContext& g = *GImGui;
4700
ImGuiWindowFlags flags = window->Flags;
4701
if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4702
return;
4703
if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4704
return;
4705
4706
const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4707
const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4708
const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f);
4709
const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4710
4711
ImVec2 pos_target(FLT_MAX, FLT_MAX);
4712
ImVec2 size_target(FLT_MAX, FLT_MAX);
4713
4714
// Manual resize grips
4715
PushID("#RESIZE");
4716
for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4717
{
4718
const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4719
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4720
4721
// Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4722
ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4723
if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4724
if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4725
bool hovered, held;
4726
ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4727
//GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4728
if (hovered || held)
4729
g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4730
4731
if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4732
{
4733
// Manual auto-fit when double-clicking
4734
size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4735
ClearActiveID();
4736
}
4737
else if (held)
4738
{
4739
// Resize from any of the four corners
4740
// We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4741
ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
4742
CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
4743
}
4744
if (resize_grip_n == 0 || held || hovered)
4745
resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4746
}
4747
for (int border_n = 0; border_n < resize_border_count; border_n++)
4748
{
4749
bool hovered, held;
4750
ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4751
ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4752
//GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4753
if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4754
{
4755
g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4756
if (held)
4757
*border_held = border_n;
4758
}
4759
if (held)
4760
{
4761
ImVec2 border_target = window->Pos;
4762
ImVec2 border_posn;
4763
if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top
4764
if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
4765
if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
4766
if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
4767
CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4768
}
4769
}
4770
PopID();
4771
4772
// Navigation resize (keyboard/gamepad)
4773
if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4774
{
4775
ImVec2 nav_resize_delta;
4776
if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4777
nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4778
if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4779
nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4780
if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4781
{
4782
const float NAV_RESIZE_SPEED = 600.0f;
4783
nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4784
g.NavWindowingToggleLayer = false;
4785
g.NavDisableMouseHover = true;
4786
resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4787
// FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4788
size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4789
}
4790
}
4791
4792
// Apply back modified position/size to window
4793
if (size_target.x != FLT_MAX)
4794
{
4795
window->SizeFull = size_target;
4796
MarkIniSettingsDirty(window);
4797
}
4798
if (pos_target.x != FLT_MAX)
4799
{
4800
window->Pos = ImFloor(pos_target);
4801
MarkIniSettingsDirty(window);
4802
}
4803
4804
window->Size = window->SizeFull;
4805
}
4806
4807
static void ImGui::RenderOuterBorders(ImGuiWindow* window)
4808
{
4809
ImGuiContext& g = *GImGui;
4810
float rounding = window->WindowRounding;
4811
float border_size = window->WindowBorderSize;
4812
if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
4813
window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
4814
4815
int border_held = window->ResizeBorderHeld;
4816
if (border_held != -1)
4817
{
4818
struct ImGuiResizeBorderDef
4819
{
4820
ImVec2 InnerDir;
4821
ImVec2 CornerPosN1, CornerPosN2;
4822
float OuterAngle;
4823
};
4824
static const ImGuiResizeBorderDef resize_border_def[4] =
4825
{
4826
{ ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
4827
{ ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
4828
{ ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
4829
{ ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left
4830
};
4831
const ImGuiResizeBorderDef& def = resize_border_def[border_held];
4832
ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
4833
window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle);
4834
window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f);
4835
window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
4836
}
4837
if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
4838
{
4839
float y = window->Pos.y + window->TitleBarHeight() - 1;
4840
window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize);
4841
}
4842
}
4843
4844
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4845
{
4846
window->ParentWindow = parent_window;
4847
window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4848
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4849
window->RootWindow = parent_window->RootWindow;
4850
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4851
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4852
while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4853
{
4854
IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
4855
window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4856
}
4857
}
4858
4859
// Push a new ImGui window to add widgets to.
4860
// - 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.
4861
// - Begin/End can be called multiple times during the frame with the same window name to append content.
4862
// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4863
// 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.
4864
// - 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.
4865
// - 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.
4866
bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4867
{
4868
ImGuiContext& g = *GImGui;
4869
const ImGuiStyle& style = g.Style;
4870
IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
4871
IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame()
4872
IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4873
4874
// Find or create
4875
ImGuiWindow* window = FindWindowByName(name);
4876
const bool window_just_created = (window == NULL);
4877
if (window_just_created)
4878
{
4879
ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4880
window = CreateNewWindow(name, size_on_first_use, flags);
4881
}
4882
4883
// Automatically disable manual moving/resizing when NoInputs is set
4884
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
4885
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4886
4887
if (flags & ImGuiWindowFlags_NavFlattened)
4888
IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4889
4890
const int current_frame = g.FrameCount;
4891
const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4892
4893
// Update Flags, LastFrameActive, BeginOrderXXX fields
4894
if (first_begin_of_the_frame)
4895
window->Flags = (ImGuiWindowFlags)flags;
4896
else
4897
flags = window->Flags;
4898
4899
// 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
4900
ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4901
ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4902
IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4903
window->HasCloseButton = (p_open != NULL);
4904
4905
// Update the Appearing flag
4906
bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4907
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4908
if (flags & ImGuiWindowFlags_Popup)
4909
{
4910
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4911
window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4912
window_just_activated_by_user |= (window != popup_ref.Window);
4913
}
4914
window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4915
if (window->Appearing)
4916
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4917
4918
// Add to stack
4919
g.CurrentWindowStack.push_back(window);
4920
SetCurrentWindow(window);
4921
CheckStacksSize(window, true);
4922
if (flags & ImGuiWindowFlags_Popup)
4923
{
4924
ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4925
popup_ref.Window = window;
4926
g.BeginPopupStack.push_back(popup_ref);
4927
window->PopupId = popup_ref.PopupId;
4928
}
4929
4930
if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4931
window->NavLastIds[0] = 0;
4932
4933
// Process SetNextWindow***() calls
4934
bool window_pos_set_by_api = false;
4935
bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4936
if (g.NextWindowData.PosCond)
4937
{
4938
window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4939
if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4940
{
4941
// May be processed on the next frame if this is our first frame and we are measuring size
4942
// FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4943
window->SetWindowPosVal = g.NextWindowData.PosVal;
4944
window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4945
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4946
}
4947
else
4948
{
4949
SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4950
}
4951
}
4952
if (g.NextWindowData.SizeCond)
4953
{
4954
window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4955
window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4956
SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4957
}
4958
if (g.NextWindowData.ContentSizeCond)
4959
{
4960
// Adjust passed "client size" to become a "window size"
4961
window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4962
if (window->SizeContentsExplicit.y != 0.0f)
4963
window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4964
}
4965
else if (first_begin_of_the_frame)
4966
{
4967
window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4968
}
4969
if (g.NextWindowData.CollapsedCond)
4970
SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4971
if (g.NextWindowData.FocusCond)
4972
FocusWindow(window);
4973
if (window->Appearing)
4974
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4975
4976
// When reusing window again multiple times a frame, just append content (don't need to setup again)
4977
if (first_begin_of_the_frame)
4978
{
4979
// Initialize
4980
const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4981
UpdateWindowParentAndRootLinks(window, flags, parent_window);
4982
4983
window->Active = true;
4984
window->BeginOrderWithinParent = 0;
4985
window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
4986
window->BeginCount = 0;
4987
window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4988
window->LastFrameActive = current_frame;
4989
window->IDStack.resize(1);
4990
4991
// Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
4992
// 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.
4993
bool window_title_visible_elsewhere = false;
4994
if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
4995
window_title_visible_elsewhere = true;
4996
if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
4997
{
4998
size_t buf_len = (size_t)window->NameBufLen;
4999
window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5000
window->NameBufLen = (int)buf_len;
5001
}
5002
5003
// UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5004
5005
// Update contents size from last frame for auto-fitting (or use explicit size)
5006
window->SizeContents = CalcSizeContents(window);
5007
if (window->HiddenFramesRegular > 0)
5008
window->HiddenFramesRegular--;
5009
if (window->HiddenFramesForResize > 0)
5010
window->HiddenFramesForResize--;
5011
5012
// Hide new windows for one frame until they calculate their size
5013
if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5014
window->HiddenFramesForResize = 1;
5015
5016
// Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5017
// We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5018
if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5019
{
5020
window->HiddenFramesForResize = 1;
5021
if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5022
{
5023
if (!window_size_x_set_by_api)
5024
window->Size.x = window->SizeFull.x = 0.f;
5025
if (!window_size_y_set_by_api)
5026
window->Size.y = window->SizeFull.y = 0.f;
5027
window->SizeContents = ImVec2(0.f, 0.f);
5028
}
5029
}
5030
5031
SetCurrentWindow(window);
5032
5033
// Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
5034
window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5035
window->WindowPadding = style.WindowPadding;
5036
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5037
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5038
window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5039
window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5040
5041
// Collapse window by double-clicking on title bar
5042
// 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
5043
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5044
{
5045
// We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
5046
ImRect title_bar_rect = window->TitleBarRect();
5047
if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5048
window->WantCollapseToggle = true;
5049
if (window->WantCollapseToggle)
5050
{
5051
window->Collapsed = !window->Collapsed;
5052
MarkIniSettingsDirty(window);
5053
FocusWindow(window);
5054
}
5055
}
5056
else
5057
{
5058
window->Collapsed = false;
5059
}
5060
window->WantCollapseToggle = false;
5061
5062
// SIZE
5063
5064
// Calculate auto-fit size, handle automatic resize
5065
const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
5066
ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
5067
if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5068
{
5069
// Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5070
if (!window_size_x_set_by_api)
5071
window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
5072
if (!window_size_y_set_by_api)
5073
window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
5074
}
5075
else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5076
{
5077
// Auto-fit may only grow window during the first few frames
5078
// We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5079
if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5080
window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5081
if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5082
window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5083
if (!window->Collapsed)
5084
MarkIniSettingsDirty(window);
5085
}
5086
5087
// Apply minimum/maximum window size constraints and final size
5088
window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
5089
window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5090
5091
// SCROLLBAR STATUS
5092
5093
// Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
5094
if (!window->Collapsed)
5095
{
5096
// When reading the current size we need to read it after size constraints have been applied
5097
float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
5098
float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
5099
window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5100
window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
5101
if (window->ScrollbarX && !window->ScrollbarY)
5102
window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
5103
window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5104
}
5105
5106
// POSITION
5107
5108
// Popup latch its initial position, will position itself when it appears next frame
5109
if (window_just_activated_by_user)
5110
{
5111
window->AutoPosLastDirection = ImGuiDir_None;
5112
if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5113
window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5114
}
5115
5116
// Position child window
5117
if (flags & ImGuiWindowFlags_ChildWindow)
5118
{
5119
IM_ASSERT(parent_window && parent_window->Active);
5120
window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5121
parent_window->DC.ChildWindows.push_back(window);
5122
if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5123
window->Pos = parent_window->DC.CursorPos;
5124
}
5125
5126
const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
5127
if (window_pos_with_pivot)
5128
SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
5129
else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5130
window->Pos = FindBestWindowPosForPopup(window);
5131
else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5132
window->Pos = FindBestWindowPosForPopup(window);
5133
else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5134
window->Pos = FindBestWindowPosForPopup(window);
5135
5136
// Clamp position so it stays visible
5137
// Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5138
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5139
{
5140
if (g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5141
{
5142
ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5143
ImVec2 size_for_clamping = ((g.IO.ConfigWindowsMoveFromTitleBarOnly) && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5144
window->Pos = ImMax(window->Pos + size_for_clamping, padding) - size_for_clamping;
5145
window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
5146
}
5147
}
5148
window->Pos = ImFloor(window->Pos);
5149
5150
// Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5151
window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5152
5153
// Prepare for item focus requests
5154
window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
5155
window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
5156
window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
5157
window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
5158
5159
// Apply scrolling
5160
window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5161
window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5162
5163
// Apply window focus (new and reactivated windows are moved to front)
5164
bool want_focus = false;
5165
if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5166
{
5167
if (flags & ImGuiWindowFlags_Popup)
5168
want_focus = true;
5169
else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5170
want_focus = true;
5171
}
5172
5173
// Handle manual resize: Resize Grips, Borders, Gamepad
5174
int border_held = -1;
5175
ImU32 resize_grip_col[4] = { 0 };
5176
const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4
5177
const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5178
if (!window->Collapsed)
5179
UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
5180
window->ResizeBorderHeld = (signed char)border_held;
5181
5182
// Default item width. Make it proportional to window size if window manually resizes
5183
if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5184
window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
5185
else
5186
window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
5187
5188
// DRAWING
5189
5190
// Setup draw list and outer clipping rectangle
5191
window->DrawList->Clear();
5192
window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
5193
window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5194
ImRect viewport_rect(GetViewportRect());
5195
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5196
PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
5197
else
5198
PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
5199
5200
// Draw modal window background (darkens what is behind them, all viewports)
5201
const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
5202
const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5203
if (dim_bg_for_modal || dim_bg_for_window_list)
5204
{
5205
const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5206
window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5207
}
5208
5209
// Draw navigation selection/windowing rectangle background
5210
if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5211
{
5212
ImRect bb = window->Rect();
5213
bb.Expand(g.FontSize);
5214
if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5215
window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5216
}
5217
5218
// Draw window + handle manual resize
5219
// As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame.
5220
const float window_rounding = window->WindowRounding;
5221
const float window_border_size = window->WindowBorderSize;
5222
const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5223
const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5224
const ImRect title_bar_rect = window->TitleBarRect();
5225
if (window->Collapsed)
5226
{
5227
// Title bar only
5228
float backup_border_size = style.FrameBorderSize;
5229
g.Style.FrameBorderSize = window->WindowBorderSize;
5230
ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5231
RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5232
g.Style.FrameBorderSize = backup_border_size;
5233
}
5234
else
5235
{
5236
// Window background
5237
if (!(flags & ImGuiWindowFlags_NoBackground))
5238
{
5239
ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5240
float alpha = 1.0f;
5241
if (g.NextWindowData.BgAlphaCond != 0)
5242
alpha = g.NextWindowData.BgAlphaVal;
5243
if (alpha != 1.0f)
5244
bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5245
window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5246
}
5247
g.NextWindowData.BgAlphaCond = 0;
5248
5249
// Title bar
5250
if (!(flags & ImGuiWindowFlags_NoTitleBar))
5251
{
5252
ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5253
window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5254
}
5255
5256
// Menu bar
5257
if (flags & ImGuiWindowFlags_MenuBar)
5258
{
5259
ImRect menu_bar_rect = window->MenuBarRect();
5260
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.
5261
window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5262
if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5263
window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5264
}
5265
5266
// Scrollbars
5267
if (window->ScrollbarX)
5268
Scrollbar(ImGuiLayoutType_Horizontal);
5269
if (window->ScrollbarY)
5270
Scrollbar(ImGuiLayoutType_Vertical);
5271
5272
// Render resize grips (after their input handling so we don't have a frame of latency)
5273
if (!(flags & ImGuiWindowFlags_NoResize))
5274
{
5275
for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5276
{
5277
const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5278
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5279
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
5280
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
5281
window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
5282
window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5283
}
5284
}
5285
5286
// Borders
5287
RenderOuterBorders(window);
5288
}
5289
5290
// Draw navigation selection/windowing rectangle border
5291
if (g.NavWindowingTargetAnim == window)
5292
{
5293
float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5294
ImRect bb = window->Rect();
5295
bb.Expand(g.FontSize);
5296
if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5297
{
5298
bb.Expand(-g.FontSize - 1.0f);
5299
rounding = window->WindowRounding;
5300
}
5301
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5302
}
5303
5304
// Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
5305
window->SizeFullAtLastBegin = window->SizeFull;
5306
5307
// Update various regions. Variables they depends on are set above in this function.
5308
// FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5309
window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5310
window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
5311
window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
5312
window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
5313
5314
// Setup drawing context
5315
// (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.)
5316
window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5317
window->DC.GroupOffset.x = 0.0f;
5318
window->DC.ColumnsOffset.x = 0.0f;
5319
window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
5320
window->DC.CursorPos = window->DC.CursorStartPos;
5321
window->DC.CursorPosPrevLine = window->DC.CursorPos;
5322
window->DC.CursorMaxPos = window->DC.CursorStartPos;
5323
window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5324
window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5325
window->DC.NavHideHighlightOneFrame = false;
5326
window->DC.NavHasScroll = (GetWindowScrollMaxY(window) > 0.0f);
5327
window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5328
window->DC.NavLayerActiveMaskNext = 0x00;
5329
window->DC.MenuBarAppending = false;
5330
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
5331
window->DC.ChildWindows.resize(0);
5332
window->DC.LayoutType = ImGuiLayoutType_Vertical;
5333
window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5334
window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5335
window->DC.ItemWidth = window->ItemWidthDefault;
5336
window->DC.TextWrapPos = -1.0f; // disabled
5337
window->DC.ItemFlagsStack.resize(0);
5338
window->DC.ItemWidthStack.resize(0);
5339
window->DC.TextWrapPosStack.resize(0);
5340
window->DC.ColumnsSet = NULL;
5341
window->DC.TreeDepth = 0;
5342
window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
5343
window->DC.StateStorage = &window->StateStorage;
5344
window->DC.GroupStack.resize(0);
5345
window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5346
5347
if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5348
{
5349
window->DC.ItemFlags = parent_window->DC.ItemFlags;
5350
window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5351
}
5352
5353
if (window->AutoFitFramesX > 0)
5354
window->AutoFitFramesX--;
5355
if (window->AutoFitFramesY > 0)
5356
window->AutoFitFramesY--;
5357
5358
// Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5359
if (want_focus)
5360
{
5361
FocusWindow(window);
5362
NavInitWindow(window, false);
5363
}
5364
5365
// Title bar
5366
if (!(flags & ImGuiWindowFlags_NoTitleBar))
5367
{
5368
// Close & collapse button are on layer 1 (same as menus) and don't default focus
5369
const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5370
window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5371
window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5372
window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5373
5374
// Collapse button
5375
if (!(flags & ImGuiWindowFlags_NoCollapse))
5376
if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
5377
window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
5378
5379
// Close button
5380
if (p_open != NULL)
5381
{
5382
const float pad = style.FramePadding.y;
5383
const float rad = g.FontSize * 0.5f;
5384
if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
5385
*p_open = false;
5386
}
5387
5388
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5389
window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5390
window->DC.ItemFlags = item_flags_backup;
5391
5392
// Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5393
// FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code..
5394
const char* UNSAVED_DOCUMENT_MARKER = "*";
5395
float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5396
ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5397
ImRect text_r = title_bar_rect;
5398
float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5399
float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5400
if (style.WindowTitleAlign.x > 0.0f)
5401
pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5402
text_r.Min.x += pad_left;
5403
text_r.Max.x -= pad_right;
5404
ImRect clip_rect = text_r;
5405
clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
5406
RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5407
if (flags & ImGuiWindowFlags_UnsavedDocument)
5408
{
5409
ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5410
ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f));
5411
RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect);
5412
}
5413
}
5414
5415
// Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5416
window->OuterRectClipped = window->Rect();
5417
window->OuterRectClipped.ClipWith(window->ClipRect);
5418
5419
// Pressing CTRL+C while holding on a window copy its content to the clipboard
5420
// This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.
5421
// Maybe we can support CTRL+C on every element?
5422
/*
5423
if (g.ActiveId == move_id)
5424
if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5425
LogToClipboard();
5426
*/
5427
5428
// Inner rectangle
5429
// We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5430
// Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5431
window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5432
window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5433
window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5434
window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5435
//window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5436
5437
// Inner clipping rectangle
5438
// Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5439
window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5440
window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5441
window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5442
window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5443
5444
// We fill last item data based on Title Bar, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5445
// This is useful to allow creating context menus on title bar only, etc.
5446
window->DC.LastItemId = window->MoveId;
5447
window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5448
window->DC.LastItemRect = title_bar_rect;
5449
}
5450
5451
PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5452
5453
// 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)
5454
if (first_begin_of_the_frame)
5455
window->WriteAccessed = false;
5456
5457
window->BeginCount++;
5458
g.NextWindowData.Clear();
5459
5460
if (flags & ImGuiWindowFlags_ChildWindow)
5461
{
5462
// Child window can be out of sight and have "negative" clip windows.
5463
// Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5464
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5465
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5466
if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5467
window->HiddenFramesRegular = 1;
5468
5469
// Completely hide along with parent or if parent is collapsed
5470
if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5471
window->HiddenFramesRegular = 1;
5472
}
5473
5474
// 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)
5475
if (style.Alpha <= 0.0f)
5476
window->HiddenFramesRegular = 1;
5477
5478
// Update the Hidden flag
5479
window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0);
5480
5481
// Return false if we don't intend to display anything to allow user to perform an early out optimization
5482
window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5483
5484
return !window->SkipItems;
5485
}
5486
5487
// Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5488
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5489
bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5490
{
5491
// Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file.
5492
if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5493
SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5494
5495
// Old API feature: override the window background alpha with a parameter.
5496
if (bg_alpha_override >= 0.0f)
5497
SetNextWindowBgAlpha(bg_alpha_override);
5498
5499
return Begin(name, p_open, flags);
5500
}
5501
#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5502
5503
void ImGui::End()
5504
{
5505
ImGuiContext& g = *GImGui;
5506
5507
if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow)
5508
{
5509
IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!");
5510
return; // FIXME-ERRORHANDLING
5511
}
5512
IM_ASSERT(g.CurrentWindowStack.Size > 0);
5513
5514
ImGuiWindow* window = g.CurrentWindow;
5515
5516
if (window->DC.ColumnsSet != NULL)
5517
EndColumns();
5518
PopClipRect(); // Inner window clip rectangle
5519
5520
// Stop logging
5521
if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5522
LogFinish();
5523
5524
// Pop from window stack
5525
g.CurrentWindowStack.pop_back();
5526
if (window->Flags & ImGuiWindowFlags_Popup)
5527
g.BeginPopupStack.pop_back();
5528
CheckStacksSize(window, false);
5529
SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5530
}
5531
5532
void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5533
{
5534
ImGuiContext& g = *GImGui;
5535
if (g.WindowsFocusOrder.back() == window)
5536
return;
5537
for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
5538
if (g.WindowsFocusOrder[i] == window)
5539
{
5540
memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5541
g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5542
break;
5543
}
5544
}
5545
5546
void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5547
{
5548
ImGuiContext& g = *GImGui;
5549
ImGuiWindow* current_front_window = g.Windows.back();
5550
if (current_front_window == window || current_front_window->RootWindow == window)
5551
return;
5552
for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5553
if (g.Windows[i] == window)
5554
{
5555
memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5556
g.Windows[g.Windows.Size - 1] = window;
5557
break;
5558
}
5559
}
5560
5561
void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5562
{
5563
ImGuiContext& g = *GImGui;
5564
if (g.Windows[0] == window)
5565
return;
5566
for (int i = 0; i < g.Windows.Size; i++)
5567
if (g.Windows[i] == window)
5568
{
5569
memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5570
g.Windows[0] = window;
5571
break;
5572
}
5573
}
5574
5575
// Moving window to front of display and set focus (which happens to be back of our sorted list)
5576
void ImGui::FocusWindow(ImGuiWindow* window)
5577
{
5578
ImGuiContext& g = *GImGui;
5579
5580
if (g.NavWindow != window)
5581
{
5582
g.NavWindow = window;
5583
if (window && g.NavDisableMouseHover)
5584
g.NavMousePosDirty = true;
5585
g.NavInitRequest = false;
5586
g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5587
g.NavIdIsAlive = false;
5588
g.NavLayer = ImGuiNavLayer_Main;
5589
//IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
5590
}
5591
5592
// Passing NULL allow to disable keyboard focus
5593
if (!window)
5594
return;
5595
5596
// Move the root window to the top of the pile
5597
if (window->RootWindow)
5598
window = window->RootWindow;
5599
5600
// Steal focus on active widgets
5601
if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5602
if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5603
ClearActiveID();
5604
5605
// Bring to front
5606
BringWindowToFocusFront(window);
5607
if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5608
BringWindowToDisplayFront(window);
5609
}
5610
5611
void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
5612
{
5613
ImGuiContext& g = *GImGui;
5614
for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
5615
{
5616
// 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.
5617
ImGuiWindow* window = g.WindowsFocusOrder[i];
5618
if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5619
if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5620
{
5621
ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5622
FocusWindow(focus_window);
5623
return;
5624
}
5625
}
5626
}
5627
5628
void ImGui::PushItemWidth(float item_width)
5629
{
5630
ImGuiWindow* window = GetCurrentWindow();
5631
window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5632
window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5633
}
5634
5635
void ImGui::PushMultiItemsWidths(int components, float w_full)
5636
{
5637
ImGuiWindow* window = GetCurrentWindow();
5638
const ImGuiStyle& style = GImGui->Style;
5639
if (w_full <= 0.0f)
5640
w_full = CalcItemWidth();
5641
const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5642
const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5643
window->DC.ItemWidthStack.push_back(w_item_last);
5644
for (int i = 0; i < components-1; i++)
5645
window->DC.ItemWidthStack.push_back(w_item_one);
5646
window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5647
}
5648
5649
void ImGui::PopItemWidth()
5650
{
5651
ImGuiWindow* window = GetCurrentWindow();
5652
window->DC.ItemWidthStack.pop_back();
5653
window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5654
}
5655
5656
float ImGui::CalcItemWidth()
5657
{
5658
ImGuiWindow* window = GetCurrentWindowRead();
5659
float w = window->DC.ItemWidth;
5660
if (w < 0.0f)
5661
{
5662
// Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
5663
float width_to_right_edge = GetContentRegionAvail().x;
5664
w = ImMax(1.0f, width_to_right_edge + w);
5665
}
5666
w = (float)(int)w;
5667
return w;
5668
}
5669
5670
void ImGui::SetCurrentFont(ImFont* font)
5671
{
5672
ImGuiContext& g = *GImGui;
5673
IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5674
IM_ASSERT(font->Scale > 0.0f);
5675
g.Font = font;
5676
g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
5677
g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5678
5679
ImFontAtlas* atlas = g.Font->ContainerAtlas;
5680
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5681
g.DrawListSharedData.Font = g.Font;
5682
g.DrawListSharedData.FontSize = g.FontSize;
5683
}
5684
5685
void ImGui::PushFont(ImFont* font)
5686
{
5687
ImGuiContext& g = *GImGui;
5688
if (!font)
5689
font = GetDefaultFont();
5690
SetCurrentFont(font);
5691
g.FontStack.push_back(font);
5692
g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5693
}
5694
5695
void ImGui::PopFont()
5696
{
5697
ImGuiContext& g = *GImGui;
5698
g.CurrentWindow->DrawList->PopTextureID();
5699
g.FontStack.pop_back();
5700
SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5701
}
5702
5703
void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5704
{
5705
ImGuiWindow* window = GetCurrentWindow();
5706
if (enabled)
5707
window->DC.ItemFlags |= option;
5708
else
5709
window->DC.ItemFlags &= ~option;
5710
window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5711
}
5712
5713
void ImGui::PopItemFlag()
5714
{
5715
ImGuiWindow* window = GetCurrentWindow();
5716
window->DC.ItemFlagsStack.pop_back();
5717
window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5718
}
5719
5720
// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
5721
void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5722
{
5723
PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
5724
}
5725
5726
void ImGui::PopAllowKeyboardFocus()
5727
{
5728
PopItemFlag();
5729
}
5730
5731
void ImGui::PushButtonRepeat(bool repeat)
5732
{
5733
PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5734
}
5735
5736
void ImGui::PopButtonRepeat()
5737
{
5738
PopItemFlag();
5739
}
5740
5741
void ImGui::PushTextWrapPos(float wrap_pos_x)
5742
{
5743
ImGuiWindow* window = GetCurrentWindow();
5744
window->DC.TextWrapPos = wrap_pos_x;
5745
window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5746
}
5747
5748
void ImGui::PopTextWrapPos()
5749
{
5750
ImGuiWindow* window = GetCurrentWindow();
5751
window->DC.TextWrapPosStack.pop_back();
5752
window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5753
}
5754
5755
// 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
5756
void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5757
{
5758
ImGuiContext& g = *GImGui;
5759
ImGuiColorMod backup;
5760
backup.Col = idx;
5761
backup.BackupValue = g.Style.Colors[idx];
5762
g.ColorModifiers.push_back(backup);
5763
g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5764
}
5765
5766
void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5767
{
5768
ImGuiContext& g = *GImGui;
5769
ImGuiColorMod backup;
5770
backup.Col = idx;
5771
backup.BackupValue = g.Style.Colors[idx];
5772
g.ColorModifiers.push_back(backup);
5773
g.Style.Colors[idx] = col;
5774
}
5775
5776
void ImGui::PopStyleColor(int count)
5777
{
5778
ImGuiContext& g = *GImGui;
5779
while (count > 0)
5780
{
5781
ImGuiColorMod& backup = g.ColorModifiers.back();
5782
g.Style.Colors[backup.Col] = backup.BackupValue;
5783
g.ColorModifiers.pop_back();
5784
count--;
5785
}
5786
}
5787
5788
struct ImGuiStyleVarInfo
5789
{
5790
ImGuiDataType Type;
5791
ImU32 Count;
5792
ImU32 Offset;
5793
void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5794
};
5795
5796
static const ImGuiStyleVarInfo GStyleVarInfo[] =
5797
{
5798
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
5799
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
5800
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
5801
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
5802
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
5803
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
5804
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
5805
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
5806
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
5807
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
5808
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
5809
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
5810
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
5811
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
5812
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
5813
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
5814
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
5815
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
5816
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
5817
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
5818
{ ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
5819
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
5820
{ ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
5821
};
5822
5823
static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5824
{
5825
IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5826
IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5827
return &GStyleVarInfo[idx];
5828
}
5829
5830
void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5831
{
5832
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5833
if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5834
{
5835
ImGuiContext& g = *GImGui;
5836
float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5837
g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5838
*pvar = val;
5839
return;
5840
}
5841
IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5842
}
5843
5844
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5845
{
5846
const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5847
if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5848
{
5849
ImGuiContext& g = *GImGui;
5850
ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5851
g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5852
*pvar = val;
5853
return;
5854
}
5855
IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5856
}
5857
5858
void ImGui::PopStyleVar(int count)
5859
{
5860
ImGuiContext& g = *GImGui;
5861
while (count > 0)
5862
{
5863
// We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5864
ImGuiStyleMod& backup = g.StyleModifiers.back();
5865
const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5866
void* data = info->GetVarPtr(&g.Style);
5867
if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
5868
else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5869
g.StyleModifiers.pop_back();
5870
count--;
5871
}
5872
}
5873
5874
const char* ImGui::GetStyleColorName(ImGuiCol idx)
5875
{
5876
// Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5877
switch (idx)
5878
{
5879
case ImGuiCol_Text: return "Text";
5880
case ImGuiCol_TextDisabled: return "TextDisabled";
5881
case ImGuiCol_WindowBg: return "WindowBg";
5882
case ImGuiCol_ChildBg: return "ChildBg";
5883
case ImGuiCol_PopupBg: return "PopupBg";
5884
case ImGuiCol_Border: return "Border";
5885
case ImGuiCol_BorderShadow: return "BorderShadow";
5886
case ImGuiCol_FrameBg: return "FrameBg";
5887
case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5888
case ImGuiCol_FrameBgActive: return "FrameBgActive";
5889
case ImGuiCol_TitleBg: return "TitleBg";
5890
case ImGuiCol_TitleBgActive: return "TitleBgActive";
5891
case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5892
case ImGuiCol_MenuBarBg: return "MenuBarBg";
5893
case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5894
case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5895
case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5896
case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5897
case ImGuiCol_CheckMark: return "CheckMark";
5898
case ImGuiCol_SliderGrab: return "SliderGrab";
5899
case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5900
case ImGuiCol_Button: return "Button";
5901
case ImGuiCol_ButtonHovered: return "ButtonHovered";
5902
case ImGuiCol_ButtonActive: return "ButtonActive";
5903
case ImGuiCol_Header: return "Header";
5904
case ImGuiCol_HeaderHovered: return "HeaderHovered";
5905
case ImGuiCol_HeaderActive: return "HeaderActive";
5906
case ImGuiCol_Separator: return "Separator";
5907
case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5908
case ImGuiCol_SeparatorActive: return "SeparatorActive";
5909
case ImGuiCol_ResizeGrip: return "ResizeGrip";
5910
case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5911
case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5912
case ImGuiCol_Tab: return "Tab";
5913
case ImGuiCol_TabHovered: return "TabHovered";
5914
case ImGuiCol_TabActive: return "TabActive";
5915
case ImGuiCol_TabUnfocused: return "TabUnfocused";
5916
case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
5917
case ImGuiCol_PlotLines: return "PlotLines";
5918
case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5919
case ImGuiCol_PlotHistogram: return "PlotHistogram";
5920
case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5921
case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5922
case ImGuiCol_DragDropTarget: return "DragDropTarget";
5923
case ImGuiCol_NavHighlight: return "NavHighlight";
5924
case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5925
case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5926
case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5927
}
5928
IM_ASSERT(0);
5929
return "Unknown";
5930
}
5931
5932
bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5933
{
5934
if (window->RootWindow == potential_parent)
5935
return true;
5936
while (window != NULL)
5937
{
5938
if (window == potential_parent)
5939
return true;
5940
window = window->ParentWindow;
5941
}
5942
return false;
5943
}
5944
5945
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5946
{
5947
IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
5948
ImGuiContext& g = *GImGui;
5949
5950
if (flags & ImGuiHoveredFlags_AnyWindow)
5951
{
5952
if (g.HoveredWindow == NULL)
5953
return false;
5954
}
5955
else
5956
{
5957
switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5958
{
5959
case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5960
if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5961
return false;
5962
break;
5963
case ImGuiHoveredFlags_RootWindow:
5964
if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5965
return false;
5966
break;
5967
case ImGuiHoveredFlags_ChildWindows:
5968
if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5969
return false;
5970
break;
5971
default:
5972
if (g.HoveredWindow != g.CurrentWindow)
5973
return false;
5974
break;
5975
}
5976
}
5977
5978
if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5979
return false;
5980
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5981
if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5982
return false;
5983
return true;
5984
}
5985
5986
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5987
{
5988
ImGuiContext& g = *GImGui;
5989
5990
if (flags & ImGuiFocusedFlags_AnyWindow)
5991
return g.NavWindow != NULL;
5992
5993
IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5994
switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5995
{
5996
case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5997
return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5998
case ImGuiFocusedFlags_RootWindow:
5999
return g.NavWindow == g.CurrentWindow->RootWindow;
6000
case ImGuiFocusedFlags_ChildWindows:
6001
return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6002
default:
6003
return g.NavWindow == g.CurrentWindow;
6004
}
6005
}
6006
6007
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6008
// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6009
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
6010
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6011
{
6012
return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6013
}
6014
6015
float ImGui::GetWindowWidth()
6016
{
6017
ImGuiWindow* window = GImGui->CurrentWindow;
6018
return window->Size.x;
6019
}
6020
6021
float ImGui::GetWindowHeight()
6022
{
6023
ImGuiWindow* window = GImGui->CurrentWindow;
6024
return window->Size.y;
6025
}
6026
6027
ImVec2 ImGui::GetWindowPos()
6028
{
6029
ImGuiContext& g = *GImGui;
6030
ImGuiWindow* window = g.CurrentWindow;
6031
return window->Pos;
6032
}
6033
6034
void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
6035
{
6036
window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
6037
window->Scroll.x = new_scroll_x;
6038
window->DC.CursorMaxPos.x -= window->Scroll.x;
6039
}
6040
6041
void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
6042
{
6043
window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
6044
window->Scroll.y = new_scroll_y;
6045
window->DC.CursorMaxPos.y -= window->Scroll.y;
6046
}
6047
6048
void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6049
{
6050
// Test condition (NB: bit 0 is always true) and clear flags for next time
6051
if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6052
return;
6053
6054
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6055
window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6056
window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6057
6058
// Set
6059
const ImVec2 old_pos = window->Pos;
6060
window->Pos = ImFloor(pos);
6061
window->DC.CursorPos += (window->Pos - old_pos); // 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
6062
window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
6063
}
6064
6065
void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6066
{
6067
ImGuiWindow* window = GetCurrentWindowRead();
6068
SetWindowPos(window, pos, cond);
6069
}
6070
6071
void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6072
{
6073
if (ImGuiWindow* window = FindWindowByName(name))
6074
SetWindowPos(window, pos, cond);
6075
}
6076
6077
ImVec2 ImGui::GetWindowSize()
6078
{
6079
ImGuiWindow* window = GetCurrentWindowRead();
6080
return window->Size;
6081
}
6082
6083
void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6084
{
6085
// Test condition (NB: bit 0 is always true) and clear flags for next time
6086
if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6087
return;
6088
6089
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6090
window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6091
6092
// Set
6093
if (size.x > 0.0f)
6094
{
6095
window->AutoFitFramesX = 0;
6096
window->SizeFull.x = ImFloor(size.x);
6097
}
6098
else
6099
{
6100
window->AutoFitFramesX = 2;
6101
window->AutoFitOnlyGrows = false;
6102
}
6103
if (size.y > 0.0f)
6104
{
6105
window->AutoFitFramesY = 0;
6106
window->SizeFull.y = ImFloor(size.y);
6107
}
6108
else
6109
{
6110
window->AutoFitFramesY = 2;
6111
window->AutoFitOnlyGrows = false;
6112
}
6113
}
6114
6115
void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6116
{
6117
SetWindowSize(GImGui->CurrentWindow, size, cond);
6118
}
6119
6120
void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6121
{
6122
if (ImGuiWindow* window = FindWindowByName(name))
6123
SetWindowSize(window, size, cond);
6124
}
6125
6126
void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6127
{
6128
// Test condition (NB: bit 0 is always true) and clear flags for next time
6129
if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6130
return;
6131
window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6132
6133
// Set
6134
window->Collapsed = collapsed;
6135
}
6136
6137
void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6138
{
6139
SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6140
}
6141
6142
bool ImGui::IsWindowCollapsed()
6143
{
6144
ImGuiWindow* window = GetCurrentWindowRead();
6145
return window->Collapsed;
6146
}
6147
6148
bool ImGui::IsWindowAppearing()
6149
{
6150
ImGuiWindow* window = GetCurrentWindowRead();
6151
return window->Appearing;
6152
}
6153
6154
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6155
{
6156
if (ImGuiWindow* window = FindWindowByName(name))
6157
SetWindowCollapsed(window, collapsed, cond);
6158
}
6159
6160
void ImGui::SetWindowFocus()
6161
{
6162
FocusWindow(GImGui->CurrentWindow);
6163
}
6164
6165
void ImGui::SetWindowFocus(const char* name)
6166
{
6167
if (name)
6168
{
6169
if (ImGuiWindow* window = FindWindowByName(name))
6170
FocusWindow(window);
6171
}
6172
else
6173
{
6174
FocusWindow(NULL);
6175
}
6176
}
6177
6178
void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6179
{
6180
ImGuiContext& g = *GImGui;
6181
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6182
g.NextWindowData.PosVal = pos;
6183
g.NextWindowData.PosPivotVal = pivot;
6184
g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6185
}
6186
6187
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6188
{
6189
ImGuiContext& g = *GImGui;
6190
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6191
g.NextWindowData.SizeVal = size;
6192
g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6193
}
6194
6195
void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6196
{
6197
ImGuiContext& g = *GImGui;
6198
g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
6199
g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6200
g.NextWindowData.SizeCallback = custom_callback;
6201
g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6202
}
6203
6204
void ImGui::SetNextWindowContentSize(const ImVec2& size)
6205
{
6206
ImGuiContext& g = *GImGui;
6207
g.NextWindowData.ContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
6208
g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
6209
}
6210
6211
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6212
{
6213
ImGuiContext& g = *GImGui;
6214
IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6215
g.NextWindowData.CollapsedVal = collapsed;
6216
g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6217
}
6218
6219
void ImGui::SetNextWindowFocus()
6220
{
6221
ImGuiContext& g = *GImGui;
6222
g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6223
}
6224
6225
void ImGui::SetNextWindowBgAlpha(float alpha)
6226
{
6227
ImGuiContext& g = *GImGui;
6228
g.NextWindowData.BgAlphaVal = alpha;
6229
g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6230
}
6231
6232
// FIXME: This is in window space (not screen space!)
6233
ImVec2 ImGui::GetContentRegionMax()
6234
{
6235
ImGuiWindow* window = GetCurrentWindowRead();
6236
ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
6237
if (window->DC.ColumnsSet)
6238
mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
6239
return mx;
6240
}
6241
6242
ImVec2 ImGui::GetContentRegionAvail()
6243
{
6244
ImGuiWindow* window = GetCurrentWindowRead();
6245
return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
6246
}
6247
6248
float ImGui::GetContentRegionAvailWidth()
6249
{
6250
return GetContentRegionAvail().x;
6251
}
6252
6253
// In window space (not screen space!)
6254
ImVec2 ImGui::GetWindowContentRegionMin()
6255
{
6256
ImGuiWindow* window = GetCurrentWindowRead();
6257
return window->ContentsRegionRect.Min - window->Pos;
6258
}
6259
6260
ImVec2 ImGui::GetWindowContentRegionMax()
6261
{
6262
ImGuiWindow* window = GetCurrentWindowRead();
6263
return window->ContentsRegionRect.Max - window->Pos;
6264
}
6265
6266
float ImGui::GetWindowContentRegionWidth()
6267
{
6268
ImGuiWindow* window = GetCurrentWindowRead();
6269
return window->ContentsRegionRect.GetWidth();
6270
}
6271
6272
float ImGui::GetTextLineHeight()
6273
{
6274
ImGuiContext& g = *GImGui;
6275
return g.FontSize;
6276
}
6277
6278
float ImGui::GetTextLineHeightWithSpacing()
6279
{
6280
ImGuiContext& g = *GImGui;
6281
return g.FontSize + g.Style.ItemSpacing.y;
6282
}
6283
6284
float ImGui::GetFrameHeight()
6285
{
6286
ImGuiContext& g = *GImGui;
6287
return g.FontSize + g.Style.FramePadding.y * 2.0f;
6288
}
6289
6290
float ImGui::GetFrameHeightWithSpacing()
6291
{
6292
ImGuiContext& g = *GImGui;
6293
return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6294
}
6295
6296
ImDrawList* ImGui::GetWindowDrawList()
6297
{
6298
ImGuiWindow* window = GetCurrentWindow();
6299
return window->DrawList;
6300
}
6301
6302
ImFont* ImGui::GetFont()
6303
{
6304
return GImGui->Font;
6305
}
6306
6307
float ImGui::GetFontSize()
6308
{
6309
return GImGui->FontSize;
6310
}
6311
6312
ImVec2 ImGui::GetFontTexUvWhitePixel()
6313
{
6314
return GImGui->DrawListSharedData.TexUvWhitePixel;
6315
}
6316
6317
void ImGui::SetWindowFontScale(float scale)
6318
{
6319
ImGuiContext& g = *GImGui;
6320
ImGuiWindow* window = GetCurrentWindow();
6321
window->FontWindowScale = scale;
6322
g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6323
}
6324
6325
// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6326
// 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'.
6327
ImVec2 ImGui::GetCursorPos()
6328
{
6329
ImGuiWindow* window = GetCurrentWindowRead();
6330
return window->DC.CursorPos - window->Pos + window->Scroll;
6331
}
6332
6333
float ImGui::GetCursorPosX()
6334
{
6335
ImGuiWindow* window = GetCurrentWindowRead();
6336
return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6337
}
6338
6339
float ImGui::GetCursorPosY()
6340
{
6341
ImGuiWindow* window = GetCurrentWindowRead();
6342
return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6343
}
6344
6345
void ImGui::SetCursorPos(const ImVec2& local_pos)
6346
{
6347
ImGuiWindow* window = GetCurrentWindow();
6348
window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6349
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6350
}
6351
6352
void ImGui::SetCursorPosX(float x)
6353
{
6354
ImGuiWindow* window = GetCurrentWindow();
6355
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6356
window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6357
}
6358
6359
void ImGui::SetCursorPosY(float y)
6360
{
6361
ImGuiWindow* window = GetCurrentWindow();
6362
window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6363
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6364
}
6365
6366
ImVec2 ImGui::GetCursorStartPos()
6367
{
6368
ImGuiWindow* window = GetCurrentWindowRead();
6369
return window->DC.CursorStartPos - window->Pos;
6370
}
6371
6372
ImVec2 ImGui::GetCursorScreenPos()
6373
{
6374
ImGuiWindow* window = GetCurrentWindowRead();
6375
return window->DC.CursorPos;
6376
}
6377
6378
void ImGui::SetCursorScreenPos(const ImVec2& pos)
6379
{
6380
ImGuiWindow* window = GetCurrentWindow();
6381
window->DC.CursorPos = pos;
6382
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6383
}
6384
6385
float ImGui::GetScrollX()
6386
{
6387
return GImGui->CurrentWindow->Scroll.x;
6388
}
6389
6390
float ImGui::GetScrollY()
6391
{
6392
return GImGui->CurrentWindow->Scroll.y;
6393
}
6394
6395
float ImGui::GetScrollMaxX()
6396
{
6397
return GetWindowScrollMaxX(GImGui->CurrentWindow);
6398
}
6399
6400
float ImGui::GetScrollMaxY()
6401
{
6402
return GetWindowScrollMaxY(GImGui->CurrentWindow);
6403
}
6404
6405
void ImGui::SetScrollX(float scroll_x)
6406
{
6407
ImGuiWindow* window = GetCurrentWindow();
6408
window->ScrollTarget.x = scroll_x;
6409
window->ScrollTargetCenterRatio.x = 0.0f;
6410
}
6411
6412
void ImGui::SetScrollY(float scroll_y)
6413
{
6414
ImGuiWindow* window = GetCurrentWindow();
6415
window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
6416
window->ScrollTargetCenterRatio.y = 0.0f;
6417
}
6418
6419
void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
6420
{
6421
// We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
6422
ImGuiWindow* window = GetCurrentWindow();
6423
IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
6424
window->ScrollTarget.y = (float)(int)(local_y + window->Scroll.y);
6425
window->ScrollTargetCenterRatio.y = center_y_ratio;
6426
}
6427
6428
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
6429
void ImGui::SetScrollHereY(float center_y_ratio)
6430
{
6431
ImGuiWindow* window = GetCurrentWindow();
6432
float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
6433
target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
6434
SetScrollFromPosY(target_y, center_y_ratio);
6435
}
6436
6437
void ImGui::ActivateItem(ImGuiID id)
6438
{
6439
ImGuiContext& g = *GImGui;
6440
g.NavNextActivateId = id;
6441
}
6442
6443
void ImGui::SetKeyboardFocusHere(int offset)
6444
{
6445
IM_ASSERT(offset >= -1); // -1 is allowed but not below
6446
ImGuiWindow* window = GetCurrentWindow();
6447
window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6448
window->FocusIdxTabRequestNext = INT_MAX;
6449
}
6450
6451
void ImGui::SetItemDefaultFocus()
6452
{
6453
ImGuiContext& g = *GImGui;
6454
ImGuiWindow* window = g.CurrentWindow;
6455
if (!window->Appearing)
6456
return;
6457
if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6458
{
6459
g.NavInitRequest = false;
6460
g.NavInitResultId = g.NavWindow->DC.LastItemId;
6461
g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6462
NavUpdateAnyRequestFlag();
6463
if (!IsItemVisible())
6464
SetScrollHereY();
6465
}
6466
}
6467
6468
void ImGui::SetStateStorage(ImGuiStorage* tree)
6469
{
6470
ImGuiWindow* window = GImGui->CurrentWindow;
6471
window->DC.StateStorage = tree ? tree : &window->StateStorage;
6472
}
6473
6474
ImGuiStorage* ImGui::GetStateStorage()
6475
{
6476
ImGuiWindow* window = GImGui->CurrentWindow;
6477
return window->DC.StateStorage;
6478
}
6479
6480
void ImGui::PushID(const char* str_id)
6481
{
6482
ImGuiWindow* window = GImGui->CurrentWindow;
6483
window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6484
}
6485
6486
void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6487
{
6488
ImGuiWindow* window = GImGui->CurrentWindow;
6489
window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6490
}
6491
6492
void ImGui::PushID(const void* ptr_id)
6493
{
6494
ImGuiWindow* window = GImGui->CurrentWindow;
6495
window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6496
}
6497
6498
void ImGui::PushID(int int_id)
6499
{
6500
const void* ptr_id = (void*)(intptr_t)int_id;
6501
ImGuiWindow* window = GImGui->CurrentWindow;
6502
window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6503
}
6504
6505
void ImGui::PopID()
6506
{
6507
ImGuiWindow* window = GImGui->CurrentWindow;
6508
window->IDStack.pop_back();
6509
}
6510
6511
ImGuiID ImGui::GetID(const char* str_id)
6512
{
6513
ImGuiWindow* window = GImGui->CurrentWindow;
6514
return window->GetID(str_id);
6515
}
6516
6517
ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6518
{
6519
ImGuiWindow* window = GImGui->CurrentWindow;
6520
return window->GetID(str_id_begin, str_id_end);
6521
}
6522
6523
ImGuiID ImGui::GetID(const void* ptr_id)
6524
{
6525
ImGuiWindow* window = GImGui->CurrentWindow;
6526
return window->GetID(ptr_id);
6527
}
6528
6529
bool ImGui::IsRectVisible(const ImVec2& size)
6530
{
6531
ImGuiWindow* window = GImGui->CurrentWindow;;
6532
return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6533
}
6534
6535
bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6536
{
6537
ImGuiWindow* window = GImGui->CurrentWindow;;
6538
return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6539
}
6540
6541
// 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.)
6542
void ImGui::BeginGroup()
6543
{
6544
ImGuiContext& g = *GImGui;
6545
ImGuiWindow* window = GetCurrentWindow();
6546
6547
window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6548
ImGuiGroupData& group_data = window->DC.GroupStack.back();
6549
group_data.BackupCursorPos = window->DC.CursorPos;
6550
group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6551
group_data.BackupIndent = window->DC.Indent;
6552
group_data.BackupGroupOffset = window->DC.GroupOffset;
6553
group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6554
group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6555
group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6556
group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6557
group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6558
group_data.AdvanceCursor = true;
6559
6560
window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6561
window->DC.Indent = window->DC.GroupOffset;
6562
window->DC.CursorMaxPos = window->DC.CursorPos;
6563
window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6564
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6565
}
6566
6567
void ImGui::EndGroup()
6568
{
6569
ImGuiContext& g = *GImGui;
6570
ImGuiWindow* window = GetCurrentWindow();
6571
IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
6572
6573
ImGuiGroupData& group_data = window->DC.GroupStack.back();
6574
6575
ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6576
group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6577
6578
window->DC.CursorPos = group_data.BackupCursorPos;
6579
window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6580
window->DC.Indent = group_data.BackupIndent;
6581
window->DC.GroupOffset = group_data.BackupGroupOffset;
6582
window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6583
window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6584
window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6585
6586
if (group_data.AdvanceCursor)
6587
{
6588
window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
6589
ItemSize(group_bb.GetSize(), 0.0f);
6590
ItemAdd(group_bb, 0);
6591
}
6592
6593
// 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.
6594
// It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
6595
// (and if you grep for LastItemId you'll notice it is only used in that context.
6596
if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6597
window->DC.LastItemId = g.ActiveId;
6598
else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6599
window->DC.LastItemId = g.ActiveIdPreviousFrame;
6600
window->DC.LastItemRect = group_bb;
6601
6602
window->DC.GroupStack.pop_back();
6603
6604
//window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
6605
}
6606
6607
// Gets back to previous line and continue with horizontal layout
6608
// pos_x == 0 : follow right after previous item
6609
// pos_x != 0 : align to specified x position (relative to window/group left)
6610
// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
6611
// spacing_w >= 0 : enforce spacing amount
6612
void ImGui::SameLine(float pos_x, float spacing_w)
6613
{
6614
ImGuiWindow* window = GetCurrentWindow();
6615
if (window->SkipItems)
6616
return;
6617
6618
ImGuiContext& g = *GImGui;
6619
if (pos_x != 0.0f)
6620
{
6621
if (spacing_w < 0.0f) spacing_w = 0.0f;
6622
window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6623
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6624
}
6625
else
6626
{
6627
if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6628
window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6629
window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6630
}
6631
window->DC.CurrentLineSize = window->DC.PrevLineSize;
6632
window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6633
}
6634
6635
void ImGui::Indent(float indent_w)
6636
{
6637
ImGuiContext& g = *GImGui;
6638
ImGuiWindow* window = GetCurrentWindow();
6639
window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6640
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6641
}
6642
6643
void ImGui::Unindent(float indent_w)
6644
{
6645
ImGuiContext& g = *GImGui;
6646
ImGuiWindow* window = GetCurrentWindow();
6647
window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6648
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6649
}
6650
6651
//-----------------------------------------------------------------------------
6652
// [SECTION] TOOLTIPS
6653
//-----------------------------------------------------------------------------
6654
6655
void ImGui::BeginTooltip()
6656
{
6657
ImGuiContext& g = *GImGui;
6658
if (g.DragDropWithinSourceOrTarget)
6659
{
6660
// The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor)
6661
// In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6662
// Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do.
6663
//ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6664
ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6665
SetNextWindowPos(tooltip_pos);
6666
SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6667
//PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6668
BeginTooltipEx(0, true);
6669
}
6670
else
6671
{
6672
BeginTooltipEx(0, false);
6673
}
6674
}
6675
6676
// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
6677
void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6678
{
6679
ImGuiContext& g = *GImGui;
6680
char window_name[16];
6681
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6682
if (override_previous_tooltip)
6683
if (ImGuiWindow* window = FindWindowByName(window_name))
6684
if (window->Active)
6685
{
6686
// Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6687
window->Hidden = true;
6688
window->HiddenFramesRegular = 1;
6689
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6690
}
6691
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
6692
Begin(window_name, NULL, flags | extra_flags);
6693
}
6694
6695
void ImGui::EndTooltip()
6696
{
6697
IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
6698
End();
6699
}
6700
6701
void ImGui::SetTooltipV(const char* fmt, va_list args)
6702
{
6703
ImGuiContext& g = *GImGui;
6704
if (g.DragDropWithinSourceOrTarget)
6705
BeginTooltip();
6706
else
6707
BeginTooltipEx(0, true);
6708
TextV(fmt, args);
6709
EndTooltip();
6710
}
6711
6712
void ImGui::SetTooltip(const char* fmt, ...)
6713
{
6714
va_list args;
6715
va_start(args, fmt);
6716
SetTooltipV(fmt, args);
6717
va_end(args);
6718
}
6719
6720
//-----------------------------------------------------------------------------
6721
// [SECTION] POPUPS
6722
//-----------------------------------------------------------------------------
6723
6724
bool ImGui::IsPopupOpen(ImGuiID id)
6725
{
6726
ImGuiContext& g = *GImGui;
6727
return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
6728
}
6729
6730
bool ImGui::IsPopupOpen(const char* str_id)
6731
{
6732
ImGuiContext& g = *GImGui;
6733
return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6734
}
6735
6736
ImGuiWindow* ImGui::GetFrontMostPopupModal()
6737
{
6738
ImGuiContext& g = *GImGui;
6739
for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6740
if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6741
if (popup->Flags & ImGuiWindowFlags_Modal)
6742
return popup;
6743
return NULL;
6744
}
6745
6746
void ImGui::OpenPopup(const char* str_id)
6747
{
6748
ImGuiContext& g = *GImGui;
6749
OpenPopupEx(g.CurrentWindow->GetID(str_id));
6750
}
6751
6752
// Mark popup as open (toggle toward open state).
6753
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6754
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6755
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
6756
void ImGui::OpenPopupEx(ImGuiID id)
6757
{
6758
ImGuiContext& g = *GImGui;
6759
ImGuiWindow* parent_window = g.CurrentWindow;
6760
int current_stack_size = g.BeginPopupStack.Size;
6761
ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6762
popup_ref.PopupId = id;
6763
popup_ref.Window = NULL;
6764
popup_ref.ParentWindow = parent_window;
6765
popup_ref.OpenFrameCount = g.FrameCount;
6766
popup_ref.OpenParentId = parent_window->IDStack.back();
6767
popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6768
popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
6769
6770
//IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6771
if (g.OpenPopupStack.Size < current_stack_size + 1)
6772
{
6773
g.OpenPopupStack.push_back(popup_ref);
6774
}
6775
else
6776
{
6777
// Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui
6778
// would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing
6779
// situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.
6780
if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6781
{
6782
g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6783
}
6784
else
6785
{
6786
// Close child popups if any, then flag popup for open/reopen
6787
g.OpenPopupStack.resize(current_stack_size + 1);
6788
g.OpenPopupStack[current_stack_size] = popup_ref;
6789
}
6790
6791
// When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6792
// This is equivalent to what ClosePopupToLevel() does.
6793
//if (g.OpenPopupStack[current_stack_size].PopupId == id)
6794
// FocusWindow(parent_window);
6795
}
6796
}
6797
6798
bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6799
{
6800
ImGuiWindow* window = GImGui->CurrentWindow;
6801
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6802
{
6803
ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
6804
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6805
OpenPopupEx(id);
6806
return true;
6807
}
6808
return false;
6809
}
6810
6811
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6812
{
6813
ImGuiContext& g = *GImGui;
6814
if (g.OpenPopupStack.empty())
6815
return;
6816
6817
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6818
// Don't close our own child popup windows.
6819
int popup_count_to_keep = 0;
6820
if (ref_window)
6821
{
6822
// Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
6823
for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
6824
{
6825
ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep];
6826
if (!popup.Window)
6827
continue;
6828
IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6829
if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6830
continue;
6831
6832
// Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6833
bool popup_or_descendent_has_focus = false;
6834
for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++)
6835
if (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow)
6836
popup_or_descendent_has_focus = true;
6837
if (!popup_or_descendent_has_focus)
6838
break;
6839
}
6840
}
6841
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
6842
{
6843
//IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
6844
ClosePopupToLevel(popup_count_to_keep, false);
6845
}
6846
}
6847
6848
void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under)
6849
{
6850
IM_ASSERT(remaining >= 0);
6851
ImGuiContext& g = *GImGui;
6852
ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6853
g.OpenPopupStack.resize(remaining);
6854
6855
// FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely.
6856
// Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing.
6857
// The current code will set focus to the parent of the popup window which is incorrect.
6858
// It rarely manifested until now because UpdateMouseMovingWindowNewFrame() would call FocusWindow() again on the clicked window,
6859
// leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again.
6860
// However if the clicked window has the _NoMove flag set we would be left with B focused.
6861
// For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway,
6862
// but we should inspect and fix this properly.
6863
if (apply_focus_to_window_under)
6864
{
6865
if (g.NavLayer == 0)
6866
focus_window = NavRestoreLastChildNavWindow(focus_window);
6867
FocusWindow(focus_window);
6868
}
6869
}
6870
6871
// Close the popup we have begin-ed into.
6872
void ImGui::CloseCurrentPopup()
6873
{
6874
ImGuiContext& g = *GImGui;
6875
int popup_idx = g.BeginPopupStack.Size - 1;
6876
if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6877
return;
6878
6879
// Closing a menu closes its top-most parent popup (unless a modal)
6880
while (popup_idx > 0)
6881
{
6882
ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
6883
ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
6884
bool close_parent = false;
6885
if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
6886
if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
6887
close_parent = true;
6888
if (!close_parent)
6889
break;
6890
popup_idx--;
6891
}
6892
//IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
6893
ClosePopupToLevel(popup_idx, true);
6894
6895
// A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
6896
// To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
6897
// Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
6898
if (ImGuiWindow* window = g.NavWindow)
6899
window->DC.NavHideHighlightOneFrame = true;
6900
}
6901
6902
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6903
{
6904
ImGuiContext& g = *GImGui;
6905
if (!IsPopupOpen(id))
6906
{
6907
g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6908
return false;
6909
}
6910
6911
char name[20];
6912
if (extra_flags & ImGuiWindowFlags_ChildMenu)
6913
ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
6914
else
6915
ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6916
6917
bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6918
if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6919
EndPopup();
6920
6921
return is_open;
6922
}
6923
6924
bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6925
{
6926
ImGuiContext& g = *GImGui;
6927
if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
6928
{
6929
g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6930
return false;
6931
}
6932
flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
6933
return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
6934
}
6935
6936
// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
6937
// Note that popup visibility status is owned by imgui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here.
6938
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6939
{
6940
ImGuiContext& g = *GImGui;
6941
ImGuiWindow* window = g.CurrentWindow;
6942
const ImGuiID id = window->GetID(name);
6943
if (!IsPopupOpen(id))
6944
{
6945
g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6946
return false;
6947
}
6948
6949
// Center modal windows by default
6950
// FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6951
if (g.NextWindowData.PosCond == 0)
6952
SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6953
6954
flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
6955
const bool is_open = Begin(name, p_open, flags);
6956
if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6957
{
6958
EndPopup();
6959
if (is_open)
6960
ClosePopupToLevel(g.BeginPopupStack.Size, true);
6961
return false;
6962
}
6963
return is_open;
6964
}
6965
6966
void ImGui::EndPopup()
6967
{
6968
ImGuiContext& g = *GImGui;
6969
IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
6970
IM_ASSERT(g.BeginPopupStack.Size > 0);
6971
6972
// Make all menus and popups wrap around for now, may need to expose that policy.
6973
NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6974
6975
End();
6976
}
6977
6978
// This is a helper to handle the simplest case of associating one named popup to one given widget.
6979
// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6980
// You can pass a NULL str_id to use the identifier of the last item.
6981
bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6982
{
6983
ImGuiWindow* window = GImGui->CurrentWindow;
6984
ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
6985
IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6986
if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6987
OpenPopupEx(id);
6988
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6989
}
6990
6991
bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6992
{
6993
if (!str_id)
6994
str_id = "window_context";
6995
ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6996
if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6997
if (also_over_items || !IsAnyItemHovered())
6998
OpenPopupEx(id);
6999
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7000
}
7001
7002
bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
7003
{
7004
if (!str_id)
7005
str_id = "void_context";
7006
ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7007
if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7008
OpenPopupEx(id);
7009
return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7010
}
7011
7012
ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
7013
{
7014
ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7015
ImRect r_screen = GetViewportRect();
7016
r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7017
return r_screen;
7018
}
7019
7020
// 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.)
7021
// 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.
7022
ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7023
{
7024
ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7025
//GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7026
//GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7027
7028
// Combo Box policy (we want a connecting edge)
7029
if (policy == ImGuiPopupPositionPolicy_ComboBox)
7030
{
7031
const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7032
for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7033
{
7034
const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7035
if (n != -1 && dir == *last_dir) // Already tried this direction?
7036
continue;
7037
ImVec2 pos;
7038
if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
7039
if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7040
if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7041
if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7042
if (!r_outer.Contains(ImRect(pos, pos + size)))
7043
continue;
7044
*last_dir = dir;
7045
return pos;
7046
}
7047
}
7048
7049
// Default popup policy
7050
const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7051
for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7052
{
7053
const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7054
if (n != -1 && dir == *last_dir) // Already tried this direction?
7055
continue;
7056
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);
7057
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);
7058
if (avail_w < size.x || avail_h < size.y)
7059
continue;
7060
ImVec2 pos;
7061
pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7062
pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
7063
*last_dir = dir;
7064
return pos;
7065
}
7066
7067
// Fallback, try to keep within display
7068
*last_dir = ImGuiDir_None;
7069
ImVec2 pos = ref_pos;
7070
pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7071
pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7072
return pos;
7073
}
7074
7075
ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7076
{
7077
ImGuiContext& g = *GImGui;
7078
7079
ImRect r_outer = GetWindowAllowedExtentRect(window);
7080
if (window->Flags & ImGuiWindowFlags_ChildMenu)
7081
{
7082
// Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7083
// This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7084
IM_ASSERT(g.CurrentWindow == window);
7085
ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7086
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).
7087
ImRect r_avoid;
7088
if (parent_window->DC.MenuBarAppending)
7089
r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7090
else
7091
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);
7092
return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7093
}
7094
if (window->Flags & ImGuiWindowFlags_Popup)
7095
{
7096
ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7097
return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7098
}
7099
if (window->Flags & ImGuiWindowFlags_Tooltip)
7100
{
7101
// Position tooltip (always follows mouse)
7102
float sc = g.Style.MouseCursorScale;
7103
ImVec2 ref_pos = NavCalcPreferredRefPos();
7104
ImRect r_avoid;
7105
if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7106
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7107
else
7108
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
7109
ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7110
if (window->AutoPosLastDirection == ImGuiDir_None)
7111
pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
7112
return pos;
7113
}
7114
IM_ASSERT(0);
7115
return window->Pos;
7116
}
7117
7118
//-----------------------------------------------------------------------------
7119
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
7120
//-----------------------------------------------------------------------------
7121
7122
// (this section is filled in the 'viewport' and 'docking' branches)
7123
7124
//-----------------------------------------------------------------------------
7125
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7126
//-----------------------------------------------------------------------------
7127
7128
ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7129
{
7130
if (ImFabs(dx) > ImFabs(dy))
7131
return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7132
return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7133
}
7134
7135
static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7136
{
7137
if (a1 < b0)
7138
return a1 - b0;
7139
if (b1 < a0)
7140
return a0 - b1;
7141
return 0.0f;
7142
}
7143
7144
static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7145
{
7146
if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7147
{
7148
r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7149
r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7150
}
7151
else
7152
{
7153
r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7154
r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7155
}
7156
}
7157
7158
// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
7159
static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7160
{
7161
ImGuiContext& g = *GImGui;
7162
ImGuiWindow* window = g.CurrentWindow;
7163
if (g.NavLayer != window->DC.NavLayerCurrent)
7164
return false;
7165
7166
const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
7167
g.NavScoringCount++;
7168
7169
// When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7170
if (window->ParentWindow == g.NavWindow)
7171
{
7172
IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7173
if (!window->ClipRect.Contains(cand))
7174
return false;
7175
cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7176
}
7177
7178
// We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items)
7179
// For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7180
NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7181
7182
// Compute distance between boxes
7183
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7184
float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7185
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
7186
if (dby != 0.0f && dbx != 0.0f)
7187
dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7188
float dist_box = ImFabs(dbx) + ImFabs(dby);
7189
7190
// 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)
7191
float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7192
float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7193
float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7194
7195
// Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7196
ImGuiDir quadrant;
7197
float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7198
if (dbx != 0.0f || dby != 0.0f)
7199
{
7200
// For non-overlapping boxes, use distance between boxes
7201
dax = dbx;
7202
day = dby;
7203
dist_axial = dist_box;
7204
quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7205
}
7206
else if (dcx != 0.0f || dcy != 0.0f)
7207
{
7208
// For overlapping boxes with different centers, use distance between centers
7209
dax = dcx;
7210
day = dcy;
7211
dist_axial = dist_center;
7212
quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7213
}
7214
else
7215
{
7216
// 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)
7217
quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7218
}
7219
7220
#if IMGUI_DEBUG_NAV_SCORING
7221
char buf[128];
7222
if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
7223
{
7224
ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]);
7225
ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7226
draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7227
draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7228
draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
7229
draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7230
}
7231
else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7232
{
7233
if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7234
if (quadrant == g.NavMoveDir)
7235
{
7236
ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7237
ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7238
draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7239
draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7240
}
7241
}
7242
#endif
7243
7244
// Is it in the quadrant we're interesting in moving to?
7245
bool new_best = false;
7246
if (quadrant == g.NavMoveDir)
7247
{
7248
// Does it beat the current best candidate?
7249
if (dist_box < result->DistBox)
7250
{
7251
result->DistBox = dist_box;
7252
result->DistCenter = dist_center;
7253
return true;
7254
}
7255
if (dist_box == result->DistBox)
7256
{
7257
// Try using distance between center points to break ties
7258
if (dist_center < result->DistCenter)
7259
{
7260
result->DistCenter = dist_center;
7261
new_best = true;
7262
}
7263
else if (dist_center == result->DistCenter)
7264
{
7265
// Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7266
// (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),
7267
// 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.
7268
if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7269
new_best = true;
7270
}
7271
}
7272
}
7273
7274
// 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
7275
// are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7276
// 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.
7277
// 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.
7278
// Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7279
if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
7280
if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7281
if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
7282
{
7283
result->DistAxial = dist_axial;
7284
new_best = true;
7285
}
7286
7287
return new_best;
7288
}
7289
7290
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
7291
static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7292
{
7293
ImGuiContext& g = *GImGui;
7294
//if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
7295
// return;
7296
7297
const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7298
const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7299
7300
// Process Init Request
7301
if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7302
{
7303
// Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7304
if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7305
{
7306
g.NavInitResultId = id;
7307
g.NavInitResultRectRel = nav_bb_rel;
7308
}
7309
if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7310
{
7311
g.NavInitRequest = false; // Found a match, clear request
7312
NavUpdateAnyRequestFlag();
7313
}
7314
}
7315
7316
// Process Move Request (scoring for navigation)
7317
// FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7318
if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
7319
{
7320
ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7321
#if IMGUI_DEBUG_NAV_SCORING
7322
// [DEBUG] Score all items in NavWindow at all times
7323
if (!g.NavMoveRequest)
7324
g.NavMoveDir = g.NavMoveDirLast;
7325
bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7326
#else
7327
bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7328
#endif
7329
if (new_best)
7330
{
7331
result->ID = id;
7332
result->SelectScopeId = g.MultiSelectScopeId;
7333
result->Window = window;
7334
result->RectRel = nav_bb_rel;
7335
}
7336
7337
const float VISIBLE_RATIO = 0.70f;
7338
if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7339
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)
7340
if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7341
{
7342
result = &g.NavMoveResultLocalVisibleSet;
7343
result->ID = id;
7344
result->SelectScopeId = g.MultiSelectScopeId;
7345
result->Window = window;
7346
result->RectRel = nav_bb_rel;
7347
}
7348
}
7349
7350
// Update window-relative bounding box of navigated item
7351
if (g.NavId == id)
7352
{
7353
g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7354
g.NavLayer = window->DC.NavLayerCurrent;
7355
g.NavIdIsAlive = true;
7356
g.NavIdTabCounter = window->FocusIdxTabCounter;
7357
window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
7358
}
7359
}
7360
7361
bool ImGui::NavMoveRequestButNoResultYet()
7362
{
7363
ImGuiContext& g = *GImGui;
7364
return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7365
}
7366
7367
void ImGui::NavMoveRequestCancel()
7368
{
7369
ImGuiContext& g = *GImGui;
7370
g.NavMoveRequest = false;
7371
NavUpdateAnyRequestFlag();
7372
}
7373
7374
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7375
{
7376
ImGuiContext& g = *GImGui;
7377
IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7378
ImGui::NavMoveRequestCancel();
7379
g.NavMoveDir = move_dir;
7380
g.NavMoveClipDir = clip_dir;
7381
g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7382
g.NavMoveRequestFlags = move_flags;
7383
g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7384
}
7385
7386
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7387
{
7388
ImGuiContext& g = *GImGui;
7389
if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7390
return;
7391
IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7392
ImRect bb_rel = window->NavRectRel[0];
7393
7394
ImGuiDir clip_dir = g.NavMoveDir;
7395
if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7396
{
7397
bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
7398
if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7399
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7400
}
7401
if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7402
{
7403
bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7404
if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7405
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7406
}
7407
if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7408
{
7409
bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
7410
if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7411
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7412
}
7413
if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7414
{
7415
bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7416
if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7417
NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7418
}
7419
}
7420
7421
static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
7422
{
7423
ImGuiWindow* parent_window = nav_window;
7424
while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7425
parent_window = parent_window->ParentWindow;
7426
if (parent_window && parent_window != nav_window)
7427
parent_window->NavLastChildNavWindow = nav_window;
7428
}
7429
7430
// Call when we are expected to land on Layer 0 after FocusWindow()
7431
static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7432
{
7433
return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7434
}
7435
7436
static void NavRestoreLayer(ImGuiNavLayer layer)
7437
{
7438
ImGuiContext& g = *GImGui;
7439
g.NavLayer = layer;
7440
if (layer == 0)
7441
g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7442
if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7443
ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7444
else
7445
ImGui::NavInitWindow(g.NavWindow, true);
7446
}
7447
7448
static inline void ImGui::NavUpdateAnyRequestFlag()
7449
{
7450
ImGuiContext& g = *GImGui;
7451
g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7452
if (g.NavAnyRequest)
7453
IM_ASSERT(g.NavWindow != NULL);
7454
}
7455
7456
// This needs to be called before we submit any widget (aka in or before Begin)
7457
void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7458
{
7459
ImGuiContext& g = *GImGui;
7460
IM_ASSERT(window == g.NavWindow);
7461
bool init_for_nav = false;
7462
if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7463
if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7464
init_for_nav = true;
7465
if (init_for_nav)
7466
{
7467
SetNavID(0, g.NavLayer);
7468
g.NavInitRequest = true;
7469
g.NavInitRequestFromMove = false;
7470
g.NavInitResultId = 0;
7471
g.NavInitResultRectRel = ImRect();
7472
NavUpdateAnyRequestFlag();
7473
}
7474
else
7475
{
7476
g.NavId = window->NavLastIds[0];
7477
}
7478
}
7479
7480
static ImVec2 ImGui::NavCalcPreferredRefPos()
7481
{
7482
ImGuiContext& g = *GImGui;
7483
if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7484
{
7485
// Mouse (we need a fallback in case the mouse becomes invalid after being used)
7486
if (IsMousePosValid(&g.IO.MousePos))
7487
return g.IO.MousePos;
7488
return g.LastValidMousePos;
7489
}
7490
else
7491
{
7492
// When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
7493
const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7494
ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));
7495
ImRect visible_rect = GetViewportRect();
7496
return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
7497
}
7498
}
7499
7500
float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7501
{
7502
ImGuiContext& g = *GImGui;
7503
if (mode == ImGuiInputReadMode_Down)
7504
return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
7505
7506
const float t = g.IO.NavInputsDownDuration[n];
7507
if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
7508
return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7509
if (t < 0.0f)
7510
return 0.0f;
7511
if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
7512
return (t == 0.0f) ? 1.0f : 0.0f;
7513
if (mode == ImGuiInputReadMode_Repeat)
7514
return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7515
if (mode == ImGuiInputReadMode_RepeatSlow)
7516
return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7517
if (mode == ImGuiInputReadMode_RepeatFast)
7518
return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7519
return 0.0f;
7520
}
7521
7522
ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7523
{
7524
ImVec2 delta(0.0f, 0.0f);
7525
if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7526
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
7527
if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7528
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
7529
if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7530
delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7531
if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7532
delta *= slow_factor;
7533
if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7534
delta *= fast_factor;
7535
return delta;
7536
}
7537
7538
// Scroll to keep newly navigated item fully into view
7539
// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
7540
static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7541
{
7542
ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7543
//GetOverlayDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7544
if (window_rect.Contains(item_rect))
7545
return;
7546
7547
ImGuiContext& g = *GImGui;
7548
if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7549
{
7550
window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7551
window->ScrollTargetCenterRatio.x = 0.0f;
7552
}
7553
else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7554
{
7555
window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7556
window->ScrollTargetCenterRatio.x = 1.0f;
7557
}
7558
if (item_rect.Min.y < window_rect.Min.y)
7559
{
7560
window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7561
window->ScrollTargetCenterRatio.y = 0.0f;
7562
}
7563
else if (item_rect.Max.y >= window_rect.Max.y)
7564
{
7565
window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7566
window->ScrollTargetCenterRatio.y = 1.0f;
7567
}
7568
}
7569
7570
static void ImGui::NavUpdate()
7571
{
7572
ImGuiContext& g = *GImGui;
7573
g.IO.WantSetMousePos = false;
7574
#if 0
7575
if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
7576
#endif
7577
7578
// Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7579
bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7580
bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7581
if (nav_gamepad_active)
7582
if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
7583
g.NavInputSource = ImGuiInputSource_NavGamepad;
7584
7585
// Update Keyboard->Nav inputs mapping
7586
if (nav_keyboard_active)
7587
{
7588
#define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7589
NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
7590
NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
7591
NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
7592
NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7593
NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7594
NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
7595
NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7596
if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7597
if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7598
if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
7599
#undef NAV_MAP_KEY
7600
}
7601
memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7602
for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7603
g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
7604
7605
// Process navigation init request (select first/default focus)
7606
if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7607
{
7608
// Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7609
IM_ASSERT(g.NavWindow);
7610
if (g.NavInitRequestFromMove)
7611
SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7612
else
7613
SetNavID(g.NavInitResultId, g.NavLayer);
7614
g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7615
}
7616
g.NavInitRequest = false;
7617
g.NavInitRequestFromMove = false;
7618
g.NavInitResultId = 0;
7619
g.NavJustMovedToId = 0;
7620
7621
// Process navigation move request
7622
if (g.NavMoveRequest)
7623
NavUpdateMoveResult();
7624
7625
// When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7626
if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7627
{
7628
IM_ASSERT(g.NavMoveRequest);
7629
if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7630
g.NavDisableHighlight = false;
7631
g.NavMoveRequestForward = ImGuiNavForward_None;
7632
}
7633
7634
// Apply application mouse position movement, after we had a chance to process move request result.
7635
if (g.NavMousePosDirty && g.NavIdIsAlive)
7636
{
7637
// Set mouse position given our knowledge of the navigated item position from last frame
7638
if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7639
{
7640
if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7641
{
7642
g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7643
g.IO.WantSetMousePos = true;
7644
}
7645
}
7646
g.NavMousePosDirty = false;
7647
}
7648
g.NavIdIsAlive = false;
7649
g.NavJustTabbedId = 0;
7650
IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7651
7652
// Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
7653
if (g.NavWindow)
7654
NavSaveLastChildNavWindow(g.NavWindow);
7655
if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7656
g.NavWindow->NavLastChildNavWindow = NULL;
7657
7658
// Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7659
NavUpdateWindowing();
7660
7661
// Set output flags for user application
7662
g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7663
g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
7664
7665
// Process NavCancel input (to close a popup, get back to parent, clear focus)
7666
if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7667
{
7668
if (g.ActiveId != 0)
7669
{
7670
if (!(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_Cancel)))
7671
ClearActiveID();
7672
}
7673
else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7674
{
7675
// Exit child window
7676
ImGuiWindow* child_window = g.NavWindow;
7677
ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7678
IM_ASSERT(child_window->ChildId != 0);
7679
FocusWindow(parent_window);
7680
SetNavID(child_window->ChildId, 0);
7681
g.NavIdIsAlive = false;
7682
if (g.NavDisableMouseHover)
7683
g.NavMousePosDirty = true;
7684
}
7685
else if (g.OpenPopupStack.Size > 0)
7686
{
7687
// Close open popup/menu
7688
if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7689
ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
7690
}
7691
else if (g.NavLayer != 0)
7692
{
7693
// Leave the "menu" layer
7694
NavRestoreLayer(ImGuiNavLayer_Main);
7695
}
7696
else
7697
{
7698
// Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7699
if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7700
g.NavWindow->NavLastIds[0] = 0;
7701
g.NavId = 0;
7702
}
7703
}
7704
7705
// Process manual activation request
7706
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7707
if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7708
{
7709
bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7710
bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7711
if (g.ActiveId == 0 && activate_pressed)
7712
g.NavActivateId = g.NavId;
7713
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7714
g.NavActivateDownId = g.NavId;
7715
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7716
g.NavActivatePressedId = g.NavId;
7717
if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7718
g.NavInputId = g.NavId;
7719
}
7720
if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7721
g.NavDisableHighlight = true;
7722
if (g.NavActivateId != 0)
7723
IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7724
g.NavMoveRequest = false;
7725
7726
// Process programmatic activation request
7727
if (g.NavNextActivateId != 0)
7728
g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7729
g.NavNextActivateId = 0;
7730
7731
// Initiate directional inputs request
7732
const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7733
if (g.NavMoveRequestForward == ImGuiNavForward_None)
7734
{
7735
g.NavMoveDir = ImGuiDir_None;
7736
g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
7737
if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7738
{
7739
if ((allowed_dir_flags & (1<<ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7740
if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7741
if ((allowed_dir_flags & (1<<ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7742
if ((allowed_dir_flags & (1<<ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7743
}
7744
g.NavMoveClipDir = g.NavMoveDir;
7745
}
7746
else
7747
{
7748
// 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)
7749
// (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7750
IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7751
IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7752
g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7753
}
7754
7755
// Update PageUp/PageDown scroll
7756
float nav_scoring_rect_offset_y = 0.0f;
7757
if (nav_keyboard_active)
7758
nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7759
7760
// If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
7761
if (g.NavMoveDir != ImGuiDir_None)
7762
{
7763
g.NavMoveRequest = true;
7764
g.NavMoveDirLast = g.NavMoveDir;
7765
}
7766
if (g.NavMoveRequest && g.NavId == 0)
7767
{
7768
g.NavInitRequest = g.NavInitRequestFromMove = true;
7769
g.NavInitResultId = 0;
7770
g.NavDisableHighlight = false;
7771
}
7772
NavUpdateAnyRequestFlag();
7773
7774
// Scrolling
7775
if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7776
{
7777
// *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7778
ImGuiWindow* window = g.NavWindow;
7779
const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
7780
if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7781
{
7782
if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7783
SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7784
if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7785
SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7786
}
7787
7788
// *Normal* Manual scroll with NavScrollXXX keys
7789
// Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7790
ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7791
if (scroll_dir.x != 0.0f && window->ScrollbarX)
7792
{
7793
SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7794
g.NavMoveFromClampedRefRect = true;
7795
}
7796
if (scroll_dir.y != 0.0f)
7797
{
7798
SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7799
g.NavMoveFromClampedRefRect = true;
7800
}
7801
}
7802
7803
// Reset search results
7804
g.NavMoveResultLocal.Clear();
7805
g.NavMoveResultLocalVisibleSet.Clear();
7806
g.NavMoveResultOther.Clear();
7807
7808
// When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
7809
if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7810
{
7811
ImGuiWindow* window = g.NavWindow;
7812
ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7813
if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7814
{
7815
float pad = window->CalcFontSize() * 0.5f;
7816
window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item
7817
window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7818
g.NavId = 0;
7819
}
7820
g.NavMoveFromClampedRefRect = false;
7821
}
7822
7823
// 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)
7824
ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7825
g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7826
g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7827
g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7828
g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7829
IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
7830
//g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7831
g.NavScoringCount = 0;
7832
#if IMGUI_DEBUG_NAV_RECTS
7833
if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7834
if (g.NavWindow) { ImU32 col = (!g.NavWindow->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); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7835
#endif
7836
}
7837
7838
// Apply result from previous frame navigation directional move request
7839
static void ImGui::NavUpdateMoveResult()
7840
{
7841
ImGuiContext& g = *GImGui;
7842
if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7843
{
7844
// In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
7845
if (g.NavId != 0)
7846
{
7847
g.NavDisableHighlight = false;
7848
g.NavDisableMouseHover = true;
7849
}
7850
return;
7851
}
7852
7853
// Select which result to use
7854
ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7855
7856
// PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7857
if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7858
if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7859
result = &g.NavMoveResultLocalVisibleSet;
7860
7861
// Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7862
if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7863
if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7864
result = &g.NavMoveResultOther;
7865
IM_ASSERT(g.NavWindow && result->Window);
7866
7867
// Scroll to keep newly navigated item fully into view.
7868
if (g.NavLayer == 0)
7869
{
7870
ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7871
NavScrollToBringItemIntoView(result->Window, rect_abs);
7872
7873
// Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7874
ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7875
ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7876
result->RectRel.Translate(delta_scroll);
7877
7878
// Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7879
if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7880
NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7881
}
7882
7883
ClearActiveID();
7884
g.NavWindow = result->Window;
7885
if (g.NavId != result->ID)
7886
{
7887
// Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
7888
g.NavJustMovedToId = result->ID;
7889
g.NavJustMovedToSelectScopeId = result->SelectScopeId;
7890
}
7891
SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7892
g.NavMoveFromClampedRefRect = false;
7893
}
7894
7895
static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7896
{
7897
ImGuiContext& g = *GImGui;
7898
if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7899
{
7900
ImGuiWindow* window = g.NavWindow;
7901
bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7902
bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7903
if (page_up_held != page_down_held) // If either (not both) are pressed
7904
{
7905
if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7906
{
7907
// Fallback manual-scroll when window has no navigable item
7908
if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7909
SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7910
else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7911
SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7912
}
7913
else
7914
{
7915
const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7916
const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7917
float nav_scoring_rect_offset_y = 0.0f;
7918
if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7919
{
7920
nav_scoring_rect_offset_y = -page_offset_y;
7921
g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7922
g.NavMoveClipDir = ImGuiDir_Up;
7923
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7924
}
7925
else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7926
{
7927
nav_scoring_rect_offset_y = +page_offset_y;
7928
g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7929
g.NavMoveClipDir = ImGuiDir_Down;
7930
g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7931
}
7932
return nav_scoring_rect_offset_y;
7933
}
7934
}
7935
}
7936
return 0.0f;
7937
}
7938
7939
static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7940
{
7941
ImGuiContext& g = *GImGui;
7942
for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
7943
if (g.WindowsFocusOrder[i] == window)
7944
return i;
7945
return -1;
7946
}
7947
7948
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7949
{
7950
ImGuiContext& g = *GImGui;
7951
for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
7952
if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
7953
return g.WindowsFocusOrder[i];
7954
return NULL;
7955
}
7956
7957
static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7958
{
7959
ImGuiContext& g = *GImGui;
7960
IM_ASSERT(g.NavWindowingTarget);
7961
if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7962
return;
7963
7964
const int i_current = FindWindowFocusIndex(g.NavWindowingTarget);
7965
ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7966
if (!window_target)
7967
window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
7968
if (window_target) // Don't reset windowing target if there's a single window in the list
7969
g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7970
g.NavWindowingToggleLayer = false;
7971
}
7972
7973
// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
7974
static void ImGui::NavUpdateWindowing()
7975
{
7976
ImGuiContext& g = *GImGui;
7977
ImGuiWindow* apply_focus_window = NULL;
7978
bool apply_toggle_layer = false;
7979
7980
ImGuiWindow* modal_window = GetFrontMostPopupModal();
7981
if (modal_window != NULL)
7982
{
7983
g.NavWindowingTarget = NULL;
7984
return;
7985
}
7986
7987
// Fade out
7988
if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7989
{
7990
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7991
if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7992
g.NavWindowingTargetAnim = NULL;
7993
}
7994
7995
// Start CTRL-TAB or Square+L/R window selection
7996
bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7997
bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7998
if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7999
if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8000
{
8001
g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
8002
g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8003
g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8004
g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8005
}
8006
8007
// Gamepad update
8008
g.NavWindowingTimer += g.IO.DeltaTime;
8009
if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8010
{
8011
// Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise
8012
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8013
8014
// Select window to focus
8015
const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8016
if (focus_change_dir != 0)
8017
{
8018
NavUpdateWindowingHighlightWindow(focus_change_dir);
8019
g.NavWindowingHighlightAlpha = 1.0f;
8020
}
8021
8022
// Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
8023
if (!IsNavInputDown(ImGuiNavInput_Menu))
8024
{
8025
g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8026
if (g.NavWindowingToggleLayer && g.NavWindow)
8027
apply_toggle_layer = true;
8028
else if (!g.NavWindowingToggleLayer)
8029
apply_focus_window = g.NavWindowingTarget;
8030
g.NavWindowingTarget = NULL;
8031
}
8032
}
8033
8034
// Keyboard: Focus
8035
if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8036
{
8037
// Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8038
g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8039
if (IsKeyPressedMap(ImGuiKey_Tab, true))
8040
NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8041
if (!g.IO.KeyCtrl)
8042
apply_focus_window = g.NavWindowingTarget;
8043
}
8044
8045
// Keyboard: Press and Release ALT to toggle menu layer
8046
// FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
8047
if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8048
if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8049
apply_toggle_layer = true;
8050
8051
// Move window
8052
if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8053
{
8054
ImVec2 move_delta;
8055
if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8056
move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8057
if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8058
move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8059
if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8060
{
8061
const float NAV_MOVE_SPEED = 800.0f;
8062
const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
8063
g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
8064
g.NavDisableMouseHover = true;
8065
MarkIniSettingsDirty(g.NavWindowingTarget);
8066
}
8067
}
8068
8069
// Apply final focus
8070
if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8071
{
8072
g.NavDisableHighlight = false;
8073
g.NavDisableMouseHover = true;
8074
apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8075
ClosePopupsOverWindow(apply_focus_window);
8076
FocusWindow(apply_focus_window);
8077
if (apply_focus_window->NavLastIds[0] == 0)
8078
NavInitWindow(apply_focus_window, false);
8079
8080
// If the window only has a menu layer, select it directly
8081
if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8082
g.NavLayer = ImGuiNavLayer_Menu;
8083
}
8084
if (apply_focus_window)
8085
g.NavWindowingTarget = NULL;
8086
8087
// Apply menu/layer toggle
8088
if (apply_toggle_layer && g.NavWindow)
8089
{
8090
// Move to parent menu if necessary
8091
ImGuiWindow* new_nav_window = g.NavWindow;
8092
while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0
8093
&& (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8094
&& (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8095
new_nav_window = new_nav_window->ParentWindow;
8096
if (new_nav_window != g.NavWindow)
8097
{
8098
ImGuiWindow* old_nav_window = g.NavWindow;
8099
FocusWindow(new_nav_window);
8100
new_nav_window->NavLastChildNavWindow = old_nav_window;
8101
}
8102
g.NavDisableHighlight = false;
8103
g.NavDisableMouseHover = true;
8104
NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main);
8105
}
8106
}
8107
8108
// Window has already passed the IsWindowNavFocusable()
8109
static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8110
{
8111
if (window->Flags & ImGuiWindowFlags_Popup)
8112
return "(Popup)";
8113
if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8114
return "(Main menu bar)";
8115
return "(Untitled)";
8116
}
8117
8118
// Overlay displayed when using CTRL+TAB. Called by EndFrame().
8119
void ImGui::NavUpdateWindowingList()
8120
{
8121
ImGuiContext& g = *GImGui;
8122
IM_ASSERT(g.NavWindowingTarget != NULL);
8123
8124
if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8125
return;
8126
8127
if (g.NavWindowingList == NULL)
8128
g.NavWindowingList = FindWindowByName("###NavWindowingList");
8129
SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8130
SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8131
PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8132
Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8133
for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8134
{
8135
ImGuiWindow* window = g.WindowsFocusOrder[n];
8136
if (!IsWindowNavFocusable(window))
8137
continue;
8138
const char* label = window->Name;
8139
if (label == FindRenderedTextEnd(label))
8140
label = GetFallbackWindowNameForWindowingList(window);
8141
Selectable(label, g.NavWindowingTarget == window);
8142
}
8143
End();
8144
PopStyleVar();
8145
}
8146
8147
//-----------------------------------------------------------------------------
8148
// [SECTION] COLUMNS
8149
// In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
8150
//-----------------------------------------------------------------------------
8151
8152
void ImGui::NextColumn()
8153
{
8154
ImGuiWindow* window = GetCurrentWindow();
8155
if (window->SkipItems || window->DC.ColumnsSet == NULL)
8156
return;
8157
8158
ImGuiContext& g = *GImGui;
8159
PopItemWidth();
8160
PopClipRect();
8161
8162
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8163
columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8164
if (++columns->Current < columns->Count)
8165
{
8166
// Columns 1+ cancel out IndentX
8167
window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
8168
window->DrawList->ChannelsSetCurrent(columns->Current);
8169
}
8170
else
8171
{
8172
window->DC.ColumnsOffset.x = 0.0f;
8173
window->DrawList->ChannelsSetCurrent(0);
8174
columns->Current = 0;
8175
columns->LineMinY = columns->LineMaxY;
8176
}
8177
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8178
window->DC.CursorPos.y = columns->LineMinY;
8179
window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
8180
window->DC.CurrentLineTextBaseOffset = 0.0f;
8181
8182
PushColumnClipRect();
8183
PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
8184
}
8185
8186
int ImGui::GetColumnIndex()
8187
{
8188
ImGuiWindow* window = GetCurrentWindowRead();
8189
return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
8190
}
8191
8192
int ImGui::GetColumnsCount()
8193
{
8194
ImGuiWindow* window = GetCurrentWindowRead();
8195
return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
8196
}
8197
8198
static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
8199
{
8200
return offset_norm * (columns->MaxX - columns->MinX);
8201
}
8202
8203
static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
8204
{
8205
return offset / (columns->MaxX - columns->MinX);
8206
}
8207
8208
static inline float GetColumnsRectHalfWidth() { return 4.0f; }
8209
8210
static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
8211
{
8212
// Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
8213
// window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
8214
ImGuiContext& g = *GImGui;
8215
ImGuiWindow* window = g.CurrentWindow;
8216
IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
8217
IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
8218
8219
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
8220
x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
8221
if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
8222
x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
8223
8224
return x;
8225
}
8226
8227
float ImGui::GetColumnOffset(int column_index)
8228
{
8229
ImGuiWindow* window = GetCurrentWindowRead();
8230
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8231
IM_ASSERT(columns != NULL);
8232
8233
if (column_index < 0)
8234
column_index = columns->Current;
8235
IM_ASSERT(column_index < columns->Columns.Size);
8236
8237
const float t = columns->Columns[column_index].OffsetNorm;
8238
const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
8239
return x_offset;
8240
}
8241
8242
static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
8243
{
8244
if (column_index < 0)
8245
column_index = columns->Current;
8246
8247
float offset_norm;
8248
if (before_resize)
8249
offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
8250
else
8251
offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
8252
return OffsetNormToPixels(columns, offset_norm);
8253
}
8254
8255
float ImGui::GetColumnWidth(int column_index)
8256
{
8257
ImGuiWindow* window = GetCurrentWindowRead();
8258
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8259
IM_ASSERT(columns != NULL);
8260
8261
if (column_index < 0)
8262
column_index = columns->Current;
8263
return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
8264
}
8265
8266
void ImGui::SetColumnOffset(int column_index, float offset)
8267
{
8268
ImGuiContext& g = *GImGui;
8269
ImGuiWindow* window = g.CurrentWindow;
8270
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8271
IM_ASSERT(columns != NULL);
8272
8273
if (column_index < 0)
8274
column_index = columns->Current;
8275
IM_ASSERT(column_index < columns->Columns.Size);
8276
8277
const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
8278
const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
8279
8280
if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
8281
offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
8282
columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
8283
8284
if (preserve_width)
8285
SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
8286
}
8287
8288
void ImGui::SetColumnWidth(int column_index, float width)
8289
{
8290
ImGuiWindow* window = GetCurrentWindowRead();
8291
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8292
IM_ASSERT(columns != NULL);
8293
8294
if (column_index < 0)
8295
column_index = columns->Current;
8296
SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
8297
}
8298
8299
void ImGui::PushColumnClipRect(int column_index)
8300
{
8301
ImGuiWindow* window = GetCurrentWindowRead();
8302
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8303
if (column_index < 0)
8304
column_index = columns->Current;
8305
8306
PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
8307
}
8308
8309
static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
8310
{
8311
for (int n = 0; n < window->ColumnsStorage.Size; n++)
8312
if (window->ColumnsStorage[n].ID == id)
8313
return &window->ColumnsStorage[n];
8314
8315
window->ColumnsStorage.push_back(ImGuiColumnsSet());
8316
ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
8317
columns->ID = id;
8318
return columns;
8319
}
8320
8321
void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
8322
{
8323
ImGuiContext& g = *GImGui;
8324
ImGuiWindow* window = GetCurrentWindow();
8325
8326
IM_ASSERT(columns_count > 1);
8327
IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
8328
8329
// Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
8330
// In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
8331
PushID(0x11223347 + (str_id ? 0 : columns_count));
8332
ImGuiID id = window->GetID(str_id ? str_id : "columns");
8333
PopID();
8334
8335
// Acquire storage for the columns set
8336
ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
8337
IM_ASSERT(columns->ID == id);
8338
columns->Current = 0;
8339
columns->Count = columns_count;
8340
columns->Flags = flags;
8341
window->DC.ColumnsSet = columns;
8342
8343
// Set state for first column
8344
const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
8345
columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
8346
columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
8347
columns->StartPosY = window->DC.CursorPos.y;
8348
columns->StartMaxPosX = window->DC.CursorMaxPos.x;
8349
columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
8350
window->DC.ColumnsOffset.x = 0.0f;
8351
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8352
8353
// Clear data if columns count changed
8354
if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
8355
columns->Columns.resize(0);
8356
8357
// Initialize defaults
8358
columns->IsFirstFrame = (columns->Columns.Size == 0);
8359
if (columns->Columns.Size == 0)
8360
{
8361
columns->Columns.reserve(columns_count + 1);
8362
for (int n = 0; n < columns_count + 1; n++)
8363
{
8364
ImGuiColumnData column;
8365
column.OffsetNorm = n / (float)columns_count;
8366
columns->Columns.push_back(column);
8367
}
8368
}
8369
8370
for (int n = 0; n < columns_count; n++)
8371
{
8372
// Compute clipping rectangle
8373
ImGuiColumnData* column = &columns->Columns[n];
8374
float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
8375
float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
8376
column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
8377
column->ClipRect.ClipWith(window->ClipRect);
8378
}
8379
8380
window->DrawList->ChannelsSplit(columns->Count);
8381
PushColumnClipRect();
8382
PushItemWidth(GetColumnWidth() * 0.65f);
8383
}
8384
8385
void ImGui::EndColumns()
8386
{
8387
ImGuiContext& g = *GImGui;
8388
ImGuiWindow* window = GetCurrentWindow();
8389
ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8390
IM_ASSERT(columns != NULL);
8391
8392
PopItemWidth();
8393
PopClipRect();
8394
window->DrawList->ChannelsMerge();
8395
8396
columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8397
window->DC.CursorPos.y = columns->LineMaxY;
8398
if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
8399
window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent
8400
8401
// Draw columns borders and handle resize
8402
bool is_being_resized = false;
8403
if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
8404
{
8405
const float y1 = columns->StartPosY;
8406
const float y2 = window->DC.CursorPos.y;
8407
int dragging_column = -1;
8408
for (int n = 1; n < columns->Count; n++)
8409
{
8410
float x = window->Pos.x + GetColumnOffset(n);
8411
const ImGuiID column_id = columns->ID + ImGuiID(n);
8412
const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
8413
const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
8414
KeepAliveID(column_id);
8415
if (IsClippedEx(column_rect, column_id, false))
8416
continue;
8417
8418
bool hovered = false, held = false;
8419
if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
8420
{
8421
ButtonBehavior(column_rect, column_id, &hovered, &held);
8422
if (hovered || held)
8423
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
8424
if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
8425
dragging_column = n;
8426
}
8427
8428
// Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
8429
const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
8430
const float xi = (float)(int)x;
8431
window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
8432
}
8433
8434
// Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
8435
if (dragging_column != -1)
8436
{
8437
if (!columns->IsBeingResized)
8438
for (int n = 0; n < columns->Count + 1; n++)
8439
columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
8440
columns->IsBeingResized = is_being_resized = true;
8441
float x = GetDraggedColumnOffset(columns, dragging_column);
8442
SetColumnOffset(dragging_column, x);
8443
}
8444
}
8445
columns->IsBeingResized = is_being_resized;
8446
8447
window->DC.ColumnsSet = NULL;
8448
window->DC.ColumnsOffset.x = 0.0f;
8449
window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8450
}
8451
8452
// [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
8453
void ImGui::Columns(int columns_count, const char* id, bool border)
8454
{
8455
ImGuiWindow* window = GetCurrentWindow();
8456
IM_ASSERT(columns_count >= 1);
8457
8458
ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
8459
//flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
8460
if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
8461
return;
8462
8463
if (window->DC.ColumnsSet != NULL)
8464
EndColumns();
8465
8466
if (columns_count != 1)
8467
BeginColumns(id, columns_count, flags);
8468
}
8469
8470
//-----------------------------------------------------------------------------
8471
// [SECTION] DRAG AND DROP
8472
//-----------------------------------------------------------------------------
8473
8474
void ImGui::ClearDragDrop()
8475
{
8476
ImGuiContext& g = *GImGui;
8477
g.DragDropActive = false;
8478
g.DragDropPayload.Clear();
8479
g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8480
g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8481
g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8482
g.DragDropAcceptFrameCount = -1;
8483
8484
g.DragDropPayloadBufHeap.clear();
8485
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8486
}
8487
8488
// Call when current ID is active.
8489
// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
8490
bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8491
{
8492
ImGuiContext& g = *GImGui;
8493
ImGuiWindow* window = g.CurrentWindow;
8494
8495
bool source_drag_active = false;
8496
ImGuiID source_id = 0;
8497
ImGuiID source_parent_id = 0;
8498
int mouse_button = 0;
8499
if (!(flags & ImGuiDragDropFlags_SourceExtern))
8500
{
8501
source_id = window->DC.LastItemId;
8502
if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8503
return false;
8504
if (g.IO.MouseDown[mouse_button] == false)
8505
return false;
8506
8507
if (source_id == 0)
8508
{
8509
// If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8510
// A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8511
if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8512
{
8513
IM_ASSERT(0);
8514
return false;
8515
}
8516
8517
// Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8518
// We build a throwaway ID based on current ID stack + relative AABB of items in window.
8519
// THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8520
// We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8521
bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8522
if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8523
return false;
8524
source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8525
if (is_hovered)
8526
SetHoveredID(source_id);
8527
if (is_hovered && g.IO.MouseClicked[mouse_button])
8528
{
8529
SetActiveID(source_id, window);
8530
FocusWindow(window);
8531
}
8532
if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8533
g.ActiveIdAllowOverlap = is_hovered;
8534
}
8535
else
8536
{
8537
g.ActiveIdAllowOverlap = false;
8538
}
8539
if (g.ActiveId != source_id)
8540
return false;
8541
source_parent_id = window->IDStack.back();
8542
source_drag_active = IsMouseDragging(mouse_button);
8543
}
8544
else
8545
{
8546
window = NULL;
8547
source_id = ImHashStr("#SourceExtern", 0);
8548
source_drag_active = true;
8549
}
8550
8551
if (source_drag_active)
8552
{
8553
if (!g.DragDropActive)
8554
{
8555
IM_ASSERT(source_id != 0);
8556
ClearDragDrop();
8557
ImGuiPayload& payload = g.DragDropPayload;
8558
payload.SourceId = source_id;
8559
payload.SourceParentId = source_parent_id;
8560
g.DragDropActive = true;
8561
g.DragDropSourceFlags = flags;
8562
g.DragDropMouseButton = mouse_button;
8563
}
8564
g.DragDropSourceFrameCount = g.FrameCount;
8565
g.DragDropWithinSourceOrTarget = true;
8566
8567
if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8568
{
8569
// Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8570
// We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8571
BeginTooltip();
8572
if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8573
{
8574
ImGuiWindow* tooltip_window = g.CurrentWindow;
8575
tooltip_window->SkipItems = true;
8576
tooltip_window->HiddenFramesRegular = 1;
8577
}
8578
}
8579
8580
if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8581
window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8582
8583
return true;
8584
}
8585
return false;
8586
}
8587
8588
void ImGui::EndDragDropSource()
8589
{
8590
ImGuiContext& g = *GImGui;
8591
IM_ASSERT(g.DragDropActive);
8592
IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8593
8594
if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8595
EndTooltip();
8596
8597
// Discard the drag if have not called SetDragDropPayload()
8598
if (g.DragDropPayload.DataFrameCount == -1)
8599
ClearDragDrop();
8600
g.DragDropWithinSourceOrTarget = false;
8601
}
8602
8603
// Use 'cond' to choose to submit payload on drag start or every frame
8604
bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8605
{
8606
ImGuiContext& g = *GImGui;
8607
ImGuiPayload& payload = g.DragDropPayload;
8608
if (cond == 0)
8609
cond = ImGuiCond_Always;
8610
8611
IM_ASSERT(type != NULL);
8612
IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8613
IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8614
IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8615
IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
8616
8617
if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8618
{
8619
// Copy payload
8620
ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8621
g.DragDropPayloadBufHeap.resize(0);
8622
if (data_size > sizeof(g.DragDropPayloadBufLocal))
8623
{
8624
// Store in heap
8625
g.DragDropPayloadBufHeap.resize((int)data_size);
8626
payload.Data = g.DragDropPayloadBufHeap.Data;
8627
memcpy(payload.Data, data, data_size);
8628
}
8629
else if (data_size > 0)
8630
{
8631
// Store locally
8632
memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8633
payload.Data = g.DragDropPayloadBufLocal;
8634
memcpy(payload.Data, data, data_size);
8635
}
8636
else
8637
{
8638
payload.Data = NULL;
8639
}
8640
payload.DataSize = (int)data_size;
8641
}
8642
payload.DataFrameCount = g.FrameCount;
8643
8644
return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8645
}
8646
8647
bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8648
{
8649
ImGuiContext& g = *GImGui;
8650
if (!g.DragDropActive)
8651
return false;
8652
8653
ImGuiWindow* window = g.CurrentWindow;
8654
if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8655
return false;
8656
IM_ASSERT(id != 0);
8657
if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8658
return false;
8659
if (window->SkipItems)
8660
return false;
8661
8662
IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8663
g.DragDropTargetRect = bb;
8664
g.DragDropTargetId = id;
8665
g.DragDropWithinSourceOrTarget = true;
8666
return true;
8667
}
8668
8669
// We don't use BeginDragDropTargetCustom() and duplicate its code because:
8670
// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8671
// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8672
// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
8673
bool ImGui::BeginDragDropTarget()
8674
{
8675
ImGuiContext& g = *GImGui;
8676
if (!g.DragDropActive)
8677
return false;
8678
8679
ImGuiWindow* window = g.CurrentWindow;
8680
if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8681
return false;
8682
if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8683
return false;
8684
8685
const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8686
ImGuiID id = window->DC.LastItemId;
8687
if (id == 0)
8688
id = window->GetIDFromRectangle(display_rect);
8689
if (g.DragDropPayload.SourceId == id)
8690
return false;
8691
8692
IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8693
g.DragDropTargetRect = display_rect;
8694
g.DragDropTargetId = id;
8695
g.DragDropWithinSourceOrTarget = true;
8696
return true;
8697
}
8698
8699
bool ImGui::IsDragDropPayloadBeingAccepted()
8700
{
8701
ImGuiContext& g = *GImGui;
8702
return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8703
}
8704
8705
const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8706
{
8707
ImGuiContext& g = *GImGui;
8708
ImGuiWindow* window = g.CurrentWindow;
8709
ImGuiPayload& payload = g.DragDropPayload;
8710
IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8711
IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
8712
if (type != NULL && !payload.IsDataType(type))
8713
return NULL;
8714
8715
// Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8716
// NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8717
const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8718
ImRect r = g.DragDropTargetRect;
8719
float r_surface = r.GetWidth() * r.GetHeight();
8720
if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8721
{
8722
g.DragDropAcceptFlags = flags;
8723
g.DragDropAcceptIdCurr = g.DragDropTargetId;
8724
g.DragDropAcceptIdCurrRectSurface = r_surface;
8725
}
8726
8727
// Render default drop visuals
8728
payload.Preview = was_accepted_previously;
8729
flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8730
if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8731
{
8732
// FIXME-DRAG: Settle on a proper default visuals for drop target.
8733
r.Expand(3.5f);
8734
bool push_clip_rect = !window->ClipRect.Contains(r);
8735
if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8736
window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8737
if (push_clip_rect) window->DrawList->PopClipRect();
8738
}
8739
8740
g.DragDropAcceptFrameCount = g.FrameCount;
8741
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()
8742
if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8743
return NULL;
8744
8745
return &payload;
8746
}
8747
8748
const ImGuiPayload* ImGui::GetDragDropPayload()
8749
{
8750
ImGuiContext& g = *GImGui;
8751
return g.DragDropActive ? &g.DragDropPayload : NULL;
8752
}
8753
8754
// We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
8755
void ImGui::EndDragDropTarget()
8756
{
8757
ImGuiContext& g = *GImGui;
8758
IM_ASSERT(g.DragDropActive);
8759
IM_ASSERT(g.DragDropWithinSourceOrTarget);
8760
g.DragDropWithinSourceOrTarget = false;
8761
}
8762
8763
//-----------------------------------------------------------------------------
8764
// [SECTION] DOCKING
8765
//-----------------------------------------------------------------------------
8766
8767
// (this section is filled in the 'docking' branch)
8768
8769
//-----------------------------------------------------------------------------
8770
// [SECTION] LOGGING/CAPTURING
8771
//-----------------------------------------------------------------------------
8772
// All text output from the interface can be captured into tty/file/clipboard.
8773
// By default, tree nodes are automatically opened during logging.
8774
//-----------------------------------------------------------------------------
8775
8776
// Pass text data straight to log (without being displayed)
8777
void ImGui::LogText(const char* fmt, ...)
8778
{
8779
ImGuiContext& g = *GImGui;
8780
if (!g.LogEnabled)
8781
return;
8782
8783
va_list args;
8784
va_start(args, fmt);
8785
if (g.LogFile)
8786
vfprintf(g.LogFile, fmt, args);
8787
else
8788
g.LogClipboard.appendfv(fmt, args);
8789
va_end(args);
8790
}
8791
8792
// Internal version that takes a position to decide on newline placement and pad items according to their depth.
8793
// We split text into individual lines to add current tree level padding
8794
void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8795
{
8796
ImGuiContext& g = *GImGui;
8797
ImGuiWindow* window = g.CurrentWindow;
8798
8799
if (!text_end)
8800
text_end = FindRenderedTextEnd(text, text_end);
8801
8802
const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8803
if (ref_pos)
8804
window->DC.LogLinePosY = ref_pos->y;
8805
8806
const char* text_remaining = text;
8807
if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
8808
g.LogStartDepth = window->DC.TreeDepth;
8809
const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8810
for (;;)
8811
{
8812
// Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8813
const char* line_start = text_remaining;
8814
const char* line_end = ImStreolRange(line_start, text_end);
8815
const bool is_first_line = (line_start == text);
8816
const bool is_last_line = (line_end == text_end);
8817
if (!is_last_line || (line_start != line_end))
8818
{
8819
const int char_count = (int)(line_end - line_start);
8820
if (log_new_line || !is_first_line)
8821
LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start);
8822
else
8823
LogText(" %.*s", char_count, line_start);
8824
}
8825
8826
if (is_last_line)
8827
break;
8828
text_remaining = line_end + 1;
8829
}
8830
}
8831
8832
// Start logging ImGui output to TTY
8833
void ImGui::LogToTTY(int max_depth)
8834
{
8835
ImGuiContext& g = *GImGui;
8836
if (g.LogEnabled)
8837
return;
8838
ImGuiWindow* window = g.CurrentWindow;
8839
8840
IM_ASSERT(g.LogFile == NULL);
8841
g.LogFile = stdout;
8842
g.LogEnabled = true;
8843
g.LogStartDepth = window->DC.TreeDepth;
8844
if (max_depth >= 0)
8845
g.LogAutoExpandMaxDepth = max_depth;
8846
}
8847
8848
// Start logging ImGui output to given file
8849
void ImGui::LogToFile(int max_depth, const char* filename)
8850
{
8851
ImGuiContext& g = *GImGui;
8852
if (g.LogEnabled)
8853
return;
8854
ImGuiWindow* window = g.CurrentWindow;
8855
8856
if (!filename)
8857
{
8858
filename = g.IO.LogFilename;
8859
if (!filename)
8860
return;
8861
}
8862
8863
IM_ASSERT(g.LogFile == NULL);
8864
g.LogFile = ImFileOpen(filename, "ab");
8865
if (!g.LogFile)
8866
{
8867
IM_ASSERT(0);
8868
return;
8869
}
8870
g.LogEnabled = true;
8871
g.LogStartDepth = window->DC.TreeDepth;
8872
if (max_depth >= 0)
8873
g.LogAutoExpandMaxDepth = max_depth;
8874
}
8875
8876
// Start logging ImGui output to clipboard
8877
void ImGui::LogToClipboard(int max_depth)
8878
{
8879
ImGuiContext& g = *GImGui;
8880
if (g.LogEnabled)
8881
return;
8882
ImGuiWindow* window = g.CurrentWindow;
8883
8884
IM_ASSERT(g.LogFile == NULL);
8885
g.LogFile = NULL;
8886
g.LogEnabled = true;
8887
g.LogStartDepth = window->DC.TreeDepth;
8888
if (max_depth >= 0)
8889
g.LogAutoExpandMaxDepth = max_depth;
8890
}
8891
8892
void ImGui::LogFinish()
8893
{
8894
ImGuiContext& g = *GImGui;
8895
if (!g.LogEnabled)
8896
return;
8897
8898
LogText(IM_NEWLINE);
8899
if (g.LogFile != NULL)
8900
{
8901
if (g.LogFile == stdout)
8902
fflush(g.LogFile);
8903
else
8904
fclose(g.LogFile);
8905
g.LogFile = NULL;
8906
}
8907
if (g.LogClipboard.size() > 1)
8908
{
8909
SetClipboardText(g.LogClipboard.begin());
8910
g.LogClipboard.clear();
8911
}
8912
g.LogEnabled = false;
8913
}
8914
8915
// Helper to display logging buttons
8916
void ImGui::LogButtons()
8917
{
8918
ImGuiContext& g = *GImGui;
8919
8920
PushID("LogButtons");
8921
const bool log_to_tty = Button("Log To TTY"); SameLine();
8922
const bool log_to_file = Button("Log To File"); SameLine();
8923
const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8924
PushItemWidth(80.0f);
8925
PushAllowKeyboardFocus(false);
8926
SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8927
PopAllowKeyboardFocus();
8928
PopItemWidth();
8929
PopID();
8930
8931
// Start logging at the end of the function so that the buttons don't appear in the log
8932
if (log_to_tty)
8933
LogToTTY(g.LogAutoExpandMaxDepth);
8934
if (log_to_file)
8935
LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8936
if (log_to_clipboard)
8937
LogToClipboard(g.LogAutoExpandMaxDepth);
8938
}
8939
8940
//-----------------------------------------------------------------------------
8941
// [SECTION] SETTINGS
8942
//-----------------------------------------------------------------------------
8943
8944
void ImGui::MarkIniSettingsDirty()
8945
{
8946
ImGuiContext& g = *GImGui;
8947
if (g.SettingsDirtyTimer <= 0.0f)
8948
g.SettingsDirtyTimer = g.IO.IniSavingRate;
8949
}
8950
8951
void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8952
{
8953
ImGuiContext& g = *GImGui;
8954
if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8955
if (g.SettingsDirtyTimer <= 0.0f)
8956
g.SettingsDirtyTimer = g.IO.IniSavingRate;
8957
}
8958
8959
ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
8960
{
8961
ImGuiContext& g = *GImGui;
8962
g.SettingsWindows.push_back(ImGuiWindowSettings());
8963
ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8964
settings->Name = ImStrdup(name);
8965
settings->ID = ImHashStr(name, 0);
8966
return settings;
8967
}
8968
8969
ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8970
{
8971
ImGuiContext& g = *GImGui;
8972
for (int i = 0; i != g.SettingsWindows.Size; i++)
8973
if (g.SettingsWindows[i].ID == id)
8974
return &g.SettingsWindows[i];
8975
return NULL;
8976
}
8977
8978
ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
8979
{
8980
if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name, 0)))
8981
return settings;
8982
return CreateNewWindowSettings(name);
8983
}
8984
8985
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8986
{
8987
size_t file_data_size = 0;
8988
char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8989
if (!file_data)
8990
return;
8991
LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8992
ImGui::MemFree(file_data);
8993
}
8994
8995
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8996
{
8997
ImGuiContext& g = *GImGui;
8998
const ImGuiID type_hash = ImHashStr(type_name, 0);
8999
for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9000
if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9001
return &g.SettingsHandlers[handler_n];
9002
return NULL;
9003
}
9004
9005
// Zero-tolerance, no error reporting, cheap .ini parsing
9006
void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9007
{
9008
ImGuiContext& g = *GImGui;
9009
IM_ASSERT(g.Initialized);
9010
IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9011
9012
// For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9013
// 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..
9014
if (ini_size == 0)
9015
ini_size = strlen(ini_data);
9016
char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
9017
char* buf_end = buf + ini_size;
9018
memcpy(buf, ini_data, ini_size);
9019
buf[ini_size] = 0;
9020
9021
void* entry_data = NULL;
9022
ImGuiSettingsHandler* entry_handler = NULL;
9023
9024
char* line_end = NULL;
9025
for (char* line = buf; line < buf_end; line = line_end + 1)
9026
{
9027
// Skip new lines markers, then find end of the line
9028
while (*line == '\n' || *line == '\r')
9029
line++;
9030
line_end = line;
9031
while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9032
line_end++;
9033
line_end[0] = 0;
9034
if (line[0] == ';')
9035
continue;
9036
if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9037
{
9038
// Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9039
line_end[-1] = 0;
9040
const char* name_end = line_end - 1;
9041
const char* type_start = line + 1;
9042
char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
9043
const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9044
if (!type_end || !name_start)
9045
{
9046
name_start = type_start; // Import legacy entries that have no type
9047
type_start = "Window";
9048
}
9049
else
9050
{
9051
*type_end = 0; // Overwrite first ']'
9052
name_start++; // Skip second '['
9053
}
9054
entry_handler = FindSettingsHandler(type_start);
9055
entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9056
}
9057
else if (entry_handler != NULL && entry_data != NULL)
9058
{
9059
// Let type handler parse the line
9060
entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9061
}
9062
}
9063
ImGui::MemFree(buf);
9064
g.SettingsLoaded = true;
9065
}
9066
9067
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9068
{
9069
ImGuiContext& g = *GImGui;
9070
g.SettingsDirtyTimer = 0.0f;
9071
if (!ini_filename)
9072
return;
9073
9074
size_t ini_data_size = 0;
9075
const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9076
FILE* f = ImFileOpen(ini_filename, "wt");
9077
if (!f)
9078
return;
9079
fwrite(ini_data, sizeof(char), ini_data_size, f);
9080
fclose(f);
9081
}
9082
9083
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
9084
const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9085
{
9086
ImGuiContext& g = *GImGui;
9087
g.SettingsDirtyTimer = 0.0f;
9088
g.SettingsIniData.Buf.resize(0);
9089
g.SettingsIniData.Buf.push_back(0);
9090
for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9091
{
9092
ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9093
handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9094
}
9095
if (out_size)
9096
*out_size = (size_t)g.SettingsIniData.size();
9097
return g.SettingsIniData.c_str();
9098
}
9099
9100
static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9101
{
9102
ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name, 0));
9103
if (!settings)
9104
settings = ImGui::CreateNewWindowSettings(name);
9105
return (void*)settings;
9106
}
9107
9108
static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9109
{
9110
ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9111
float x, y;
9112
int i;
9113
if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
9114
else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
9115
else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
9116
}
9117
9118
static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9119
{
9120
// Gather data from windows that were active during this session
9121
// (if a window wasn't opened in this session we preserve its settings)
9122
ImGuiContext& g = *imgui_ctx;
9123
for (int i = 0; i != g.Windows.Size; i++)
9124
{
9125
ImGuiWindow* window = g.Windows[i];
9126
if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9127
continue;
9128
9129
ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
9130
if (!settings)
9131
{
9132
settings = ImGui::CreateNewWindowSettings(window->Name);
9133
window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
9134
}
9135
IM_ASSERT(settings->ID == window->ID);
9136
settings->Pos = window->Pos;
9137
settings->Size = window->SizeFull;
9138
settings->Collapsed = window->Collapsed;
9139
}
9140
9141
// Write to text buffer
9142
buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
9143
for (int i = 0; i != g.SettingsWindows.Size; i++)
9144
{
9145
const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
9146
if (settings->Pos.x == FLT_MAX)
9147
continue;
9148
const char* name = settings->Name;
9149
if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9150
name = p;
9151
buf->appendf("[%s][%s]\n", handler->TypeName, name);
9152
buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
9153
buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
9154
buf->appendf("Collapsed=%d\n", settings->Collapsed);
9155
buf->appendf("\n");
9156
}
9157
}
9158
9159
//-----------------------------------------------------------------------------
9160
// [SECTION] PLATFORM DEPENDENT HELPERS
9161
//-----------------------------------------------------------------------------
9162
9163
#if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9164
#ifndef WIN32_LEAN_AND_MEAN
9165
#define WIN32_LEAN_AND_MEAN
9166
#endif
9167
#ifndef __MINGW32__
9168
#include <Windows.h>
9169
#else
9170
#include <windows.h>
9171
#endif
9172
#endif
9173
9174
// Win32 API clipboard implementation
9175
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9176
9177
#ifdef _MSC_VER
9178
#pragma comment(lib, "user32")
9179
#endif
9180
9181
static const char* GetClipboardTextFn_DefaultImpl(void*)
9182
{
9183
static ImVector<char> buf_local;
9184
buf_local.clear();
9185
if (!::OpenClipboard(NULL))
9186
return NULL;
9187
HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9188
if (wbuf_handle == NULL)
9189
{
9190
::CloseClipboard();
9191
return NULL;
9192
}
9193
if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9194
{
9195
int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9196
buf_local.resize(buf_len);
9197
ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9198
}
9199
::GlobalUnlock(wbuf_handle);
9200
::CloseClipboard();
9201
return buf_local.Data;
9202
}
9203
9204
static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9205
{
9206
if (!::OpenClipboard(NULL))
9207
return;
9208
const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9209
HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9210
if (wbuf_handle == NULL)
9211
{
9212
::CloseClipboard();
9213
return;
9214
}
9215
ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9216
ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9217
::GlobalUnlock(wbuf_handle);
9218
::EmptyClipboard();
9219
if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9220
::GlobalFree(wbuf_handle);
9221
::CloseClipboard();
9222
}
9223
9224
#else
9225
9226
// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
9227
static const char* GetClipboardTextFn_DefaultImpl(void*)
9228
{
9229
ImGuiContext& g = *GImGui;
9230
return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9231
}
9232
9233
// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
9234
static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9235
{
9236
ImGuiContext& g = *GImGui;
9237
g.PrivateClipboard.clear();
9238
const char* text_end = text + strlen(text);
9239
g.PrivateClipboard.resize((int)(text_end - text) + 1);
9240
memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9241
g.PrivateClipboard[(int)(text_end - text)] = 0;
9242
}
9243
9244
#endif
9245
9246
// Win32 API IME support (for Asian languages, etc.)
9247
#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9248
9249
#include <imm.h>
9250
#ifdef _MSC_VER
9251
#pragma comment(lib, "imm32")
9252
#endif
9253
9254
static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9255
{
9256
// Notify OS Input Method Editor of text input position
9257
if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9258
if (HIMC himc = ::ImmGetContext(hwnd))
9259
{
9260
COMPOSITIONFORM cf;
9261
cf.ptCurrentPos.x = x;
9262
cf.ptCurrentPos.y = y;
9263
cf.dwStyle = CFS_FORCE_POSITION;
9264
::ImmSetCompositionWindow(himc, &cf);
9265
::ImmReleaseContext(hwnd, himc);
9266
}
9267
}
9268
9269
#else
9270
9271
static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9272
9273
#endif
9274
9275
//-----------------------------------------------------------------------------
9276
// [SECTION] METRICS/DEBUG WINDOW
9277
//-----------------------------------------------------------------------------
9278
9279
void ImGui::ShowMetricsWindow(bool* p_open)
9280
{
9281
if (!ImGui::Begin("ImGui Metrics", p_open))
9282
{
9283
ImGui::End();
9284
return;
9285
}
9286
9287
static bool show_draw_cmd_clip_rects = true;
9288
static bool show_window_begin_order = false;
9289
ImGuiIO& io = ImGui::GetIO();
9290
ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9291
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9292
ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9293
ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9294
ImGui::Text("%d allocations", io.MetricsActiveAllocations);
9295
ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
9296
ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
9297
ImGui::Separator();
9298
9299
struct Funcs
9300
{
9301
static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9302
{
9303
bool node_open = ImGui::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, draw_list->CmdBuffer.Size);
9304
if (draw_list == ImGui::GetWindowDrawList())
9305
{
9306
ImGui::SameLine();
9307
ImGui::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)
9308
if (node_open) ImGui::TreePop();
9309
return;
9310
}
9311
9312
ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list
9313
if (window && IsItemHovered())
9314
overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9315
if (!node_open)
9316
return;
9317
9318
int elem_offset = 0;
9319
for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9320
{
9321
if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9322
continue;
9323
if (pcmd->UserCallback)
9324
{
9325
ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9326
continue;
9327
}
9328
ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9329
bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9330
if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
9331
{
9332
ImRect clip_rect = pcmd->ClipRect;
9333
ImRect vtxs_rect;
9334
for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9335
vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9336
clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
9337
vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
9338
}
9339
if (!pcmd_node_open)
9340
continue;
9341
9342
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9343
ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9344
while (clipper.Step())
9345
for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9346
{
9347
char buf[300];
9348
char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9349
ImVec2 triangles_pos[3];
9350
for (int n = 0; n < 3; n++, idx_i++)
9351
{
9352
int vtx_i = idx_buffer ? idx_buffer[idx_i] : idx_i;
9353
ImDrawVert& v = draw_list->VtxBuffer[vtx_i];
9354
triangles_pos[n] = v.pos;
9355
buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
9356
(n == 0) ? "idx" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9357
}
9358
ImGui::Selectable(buf, false);
9359
if (ImGui::IsItemHovered())
9360
{
9361
ImDrawListFlags backup_flags = overlay_draw_list->Flags;
9362
overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
9363
overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
9364
overlay_draw_list->Flags = backup_flags;
9365
}
9366
}
9367
ImGui::TreePop();
9368
}
9369
ImGui::TreePop();
9370
}
9371
9372
static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9373
{
9374
if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9375
return;
9376
for (int i = 0; i < windows.Size; i++)
9377
Funcs::NodeWindow(windows[i], "Window");
9378
ImGui::TreePop();
9379
}
9380
9381
static void NodeWindow(ImGuiWindow* window, const char* label)
9382
{
9383
if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9384
return;
9385
ImGuiWindowFlags flags = window->Flags;
9386
NodeDrawList(window, window->DrawList, "DrawList");
9387
ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
9388
ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9389
(flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9390
(flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9391
(flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9392
ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window));
9393
ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9394
ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
9395
ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9396
ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9397
if (!window->NavRectRel[0].IsInverted())
9398
ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
9399
else
9400
ImGui::BulletText("NavRectRel[0]: <None>");
9401
if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9402
if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9403
if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9404
if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9405
{
9406
for (int n = 0; n < window->ColumnsStorage.Size; n++)
9407
{
9408
const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
9409
if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9410
{
9411
ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
9412
for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9413
ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
9414
ImGui::TreePop();
9415
}
9416
}
9417
ImGui::TreePop();
9418
}
9419
ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9420
ImGui::TreePop();
9421
}
9422
9423
static void NodeTabBar(ImGuiTabBar* tab_bar)
9424
{
9425
// Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
9426
char buf[256];
9427
char* p = buf;
9428
const char* buf_end = buf + IM_ARRAYSIZE(buf);
9429
ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9430
if (ImGui::TreeNode(tab_bar, "%s", buf))
9431
{
9432
for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9433
{
9434
const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9435
ImGui::PushID(tab);
9436
if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9437
if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9438
ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9439
ImGui::PopID();
9440
}
9441
ImGui::TreePop();
9442
}
9443
}
9444
};
9445
9446
// Access private state, we are going to display the draw lists from last frame
9447
ImGuiContext& g = *GImGui;
9448
Funcs::NodeWindows(g.Windows, "Windows");
9449
if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9450
{
9451
for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9452
Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9453
ImGui::TreePop();
9454
}
9455
if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9456
{
9457
for (int i = 0; i < g.OpenPopupStack.Size; i++)
9458
{
9459
ImGuiWindow* window = g.OpenPopupStack[i].Window;
9460
ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
9461
}
9462
ImGui::TreePop();
9463
}
9464
if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9465
{
9466
for (int n = 0; n < g.TabBars.Data.Size; n++)
9467
Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9468
ImGui::TreePop();
9469
}
9470
if (ImGui::TreeNode("Internal state"))
9471
{
9472
const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9473
ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9474
ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9475
ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
9476
ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
9477
ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9478
ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9479
ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9480
ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9481
ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9482
ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9483
ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9484
ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9485
ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9486
ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9487
ImGui::TreePop();
9488
}
9489
9490
9491
if (g.IO.KeyCtrl && show_window_begin_order)
9492
{
9493
for (int n = 0; n < g.Windows.Size; n++)
9494
{
9495
ImGuiWindow* window = g.Windows[n];
9496
if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
9497
continue;
9498
char buf[32];
9499
ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9500
float font_size = ImGui::GetFontSize() * 2;
9501
ImDrawList* overlay_draw_list = GetOverlayDrawList(window);
9502
overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9503
overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
9504
}
9505
}
9506
ImGui::End();
9507
}
9508
9509
//-----------------------------------------------------------------------------
9510
9511
// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9512
// 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.
9513
#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9514
#include "imgui_user.inl"
9515
#endif
9516
9517
//-----------------------------------------------------------------------------
9518
9519