1 // dear imgui, v1.85 WIP
2 // (main code and documentation)
3
4 // Help:
5 // - Read FAQ at http://dearimgui.org/faq
6 // - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
7 // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
8 // Read imgui.cpp for details, links and comments.
9
10 // Resources:
11 // - FAQ http://dearimgui.org/faq
12 // - Homepage & latest https://github.com/ocornut/imgui
13 // - Releases & changelog https://github.com/ocornut/imgui/releases
14 // - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!)
15 // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there)
16 // - Glossary https://github.com/ocornut/imgui/wiki/Glossary
17 // - Issues & support https://github.com/ocornut/imgui/issues
18
19 // Getting Started?
20 // - For first-time users having issues compiling/linking/running or issues loading fonts:
21 // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
22
23 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
24 // See LICENSE.txt for copyright and licensing details (standard MIT License).
25 // This library is free but needs your support to sustain development and maintenance.
26 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com".
27 // Individuals: you can support continued development via donations. See docs/README or web page.
28
29 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
30 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
31 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
32 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
33 // to a better solution or official support for them.
34
35 /*
36
37 Index of this file:
38
39 DOCUMENTATION
40
41 - MISSION STATEMENT
42 - END-USER GUIDE
43 - PROGRAMMER GUIDE
44 - READ FIRST
45 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
46 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
47 - HOW A SIMPLE APPLICATION MAY LOOK LIKE
48 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
49 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
50 - API BREAKING CHANGES (read me when you update!)
51 - FREQUENTLY ASKED QUESTIONS (FAQ)
52 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
53
54 CODE
55 (search for "[SECTION]" in the code to find them)
56
57 // [SECTION] INCLUDES
58 // [SECTION] FORWARD DECLARATIONS
59 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
60 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
61 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
62 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
63 // [SECTION] MISC HELPERS/UTILITIES (File functions)
64 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
65 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
66 // [SECTION] ImGuiStorage
67 // [SECTION] ImGuiTextFilter
68 // [SECTION] ImGuiTextBuffer
69 // [SECTION] ImGuiListClipper
70 // [SECTION] STYLING
71 // [SECTION] RENDER HELPERS
72 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
73 // [SECTION] ERROR CHECKING
74 // [SECTION] LAYOUT
75 // [SECTION] SCROLLING
76 // [SECTION] TOOLTIPS
77 // [SECTION] POPUPS
78 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
79 // [SECTION] DRAG AND DROP
80 // [SECTION] LOGGING/CAPTURING
81 // [SECTION] SETTINGS
82 // [SECTION] VIEWPORTS
83 // [SECTION] PLATFORM DEPENDENT HELPERS
84 // [SECTION] METRICS/DEBUGGER WINDOW
85
86 */
87
88 //-----------------------------------------------------------------------------
89 // DOCUMENTATION
90 //-----------------------------------------------------------------------------
91
92 /*
93
94 MISSION STATEMENT
95 =================
96
97 - Easy to use to create code-driven and data-driven tools.
98 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
99 - Easy to hack and improve.
100 - Minimize setup and maintenance.
101 - Minimize state storage on user side.
102 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
103 - Efficient runtime and memory consumption.
104
105 Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
106
107 - Doesn't look fancy, doesn't animate.
108 - Limited layout features, intricate layouts are typically crafted in code.
109
110
111 END-USER GUIDE
112 ==============
113
114 - Double-click on title bar to collapse window.
115 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
116 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
117 - Click and drag on any empty space to move window.
118 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
119 - CTRL+Click on a slider or drag box to input value as text.
120 - Use mouse wheel to scroll.
121 - Text editor:
122 - Hold SHIFT or use mouse to select text.
123 - CTRL+Left/Right to word jump.
124 - CTRL+Shift+Left/Right to select words.
125 - CTRL+A our Double-Click to select all.
126 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
127 - CTRL+Z,CTRL+Y to undo/redo.
128 - ESCAPE to revert text to its original value.
129 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
130 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
131 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
132 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
133
134
135 PROGRAMMER GUIDE
136 ================
137
138 READ FIRST
139 ----------
140 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
141 - 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 or
142 destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs.
143 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
144 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
145 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
146 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
147 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
148 For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
149 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
150 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
151 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
152 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
153 If you get an assert, read the messages and comments around the assert.
154 - 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.
155 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
156 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
157 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
158 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
159
160
161 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
162 ----------------------------------------------
163 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
164 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
165 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
166 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
167 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
168 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
169 likely be a comment about it. Please report any issue to the GitHub page!
170 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
171 - Try to keep your copy of Dear ImGui reasonably up to date.
172
173
174 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
175 ---------------------------------------------------------------
176 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
177 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
178 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
179 It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
180 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
181 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
182 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
183 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
184 phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
185 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
186 - 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.
187
188
189 HOW A SIMPLE APPLICATION MAY LOOK LIKE
190 --------------------------------------
191 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
192 The sub-folders in examples/ contain examples applications following this structure.
193
194 // Application init: create a dear imgui context, setup some options, load fonts
195 ImGui::CreateContext();
196 ImGuiIO& io = ImGui::GetIO();
197 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
198 // TODO: Fill optional fields of the io structure later.
199 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
200
201 // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
202 ImGui_ImplWin32_Init(hwnd);
203 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
204
205 // Application main loop
206 while (true)
207 {
208 // Feed inputs to dear imgui, start new frame
209 ImGui_ImplDX11_NewFrame();
210 ImGui_ImplWin32_NewFrame();
211 ImGui::NewFrame();
212
213 // Any application code here
214 ImGui::Text("Hello, world!");
215
216 // Render dear imgui into screen
217 ImGui::Render();
218 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
219 g_pSwapChain->Present(1, 0);
220 }
221
222 // Shutdown
223 ImGui_ImplDX11_Shutdown();
224 ImGui_ImplWin32_Shutdown();
225 ImGui::DestroyContext();
226
227 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
228
229 // Application init: create a dear imgui context, setup some options, load fonts
230 ImGui::CreateContext();
231 ImGuiIO& io = ImGui::GetIO();
232 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
233 // TODO: Fill optional fields of the io structure later.
234 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
235
236 // Build and load the texture atlas into a texture
237 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
238 int width, height;
239 unsigned char* pixels = NULL;
240 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
241
242 // At this point you've got the texture data and you need to upload that to your graphic system:
243 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
244 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
245 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
246 io.Fonts->SetTexID((void*)texture);
247
248 // Application main loop
249 while (true)
250 {
251 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
252 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
253 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
254 io.DisplaySize.x = 1920.0f; // set the current display width
255 io.DisplaySize.y = 1280.0f; // set the current display height here
256 io.MousePos = my_mouse_pos; // set the mouse position
257 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
258 io.MouseDown[1] = my_mouse_buttons[1];
259
260 // Call NewFrame(), after this point you can use ImGui::* functions anytime
261 // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
262 ImGui::NewFrame();
263
264 // Most of your application code here
265 ImGui::Text("Hello, world!");
266 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
267 MyGameRender(); // may use any Dear ImGui functions as well!
268
269 // Render dear imgui, swap buffers
270 // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
271 ImGui::EndFrame();
272 ImGui::Render();
273 ImDrawData* draw_data = ImGui::GetDrawData();
274 MyImGuiRenderFunction(draw_data);
275 SwapBuffers();
276 }
277
278 // Shutdown
279 ImGui::DestroyContext();
280
281 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
282 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
283 Please read the FAQ and example applications for details about this!
284
285
286 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
287 ---------------------------------------------
288 The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
289
290 void void MyImGuiRenderFunction(ImDrawData* draw_data)
291 {
292 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
293 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
294 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
295 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
296 for (int n = 0; n < draw_data->CmdListsCount; n++)
297 {
298 const ImDrawList* cmd_list = draw_data->CmdLists[n];
299 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
300 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
301 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
302 {
303 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
304 if (pcmd->UserCallback)
305 {
306 pcmd->UserCallback(cmd_list, pcmd);
307 }
308 else
309 {
310 // The texture for the draw call is specified by pcmd->GetTexID().
311 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
312 MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
313
314 // We are using scissoring to clip some objects. All low-level graphics API should support it.
315 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
316 // (some elements visible outside their bounds) but you can fix that once everything else works!
317 // - Clipping coordinates are provided in imgui coordinates space:
318 // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
319 // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.
320 // - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
321 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
322 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
323 ImVec2 pos = draw_data->DisplayPos;
324 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));
325
326 // Render 'pcmd->ElemCount/3' indexed triangles.
327 // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.
328 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
329 }
330 idx_buffer += pcmd->ElemCount;
331 }
332 }
333 }
334
335
336 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
337 ------------------------------------------
338 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
339 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
340 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
341 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
342 - Keyboard:
343 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
344 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
345 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
346 will be set. For more advanced uses, you may want to read from:
347 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
348 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
349 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
350 Please reach out if you think the game vs navigation input sharing could be improved.
351 - Gamepad:
352 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
353 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
354 Note that io.NavInputs[] is cleared by EndFrame().
355 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
356 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
357 - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
358 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.).
359 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
360 - 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
361 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
362 - Mouse:
363 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
364 - 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.
365 - 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.
366 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
367 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.
368 When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
369 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse moving back and forth!)
370 (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
371 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
372
373
374 API BREAKING CHANGES
375 ====================
376
377 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
378 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.
379 When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
380 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
381
382 - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead.
383 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
384 - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
385 - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
386 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
387 - if you are using official backends from the source tree: you have nothing to do.
388 - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
389 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
390 - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
391 - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
392 - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
393 flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
394 breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
395 - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
396 - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
397 - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
398 - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
399 this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
400 the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
401 legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
402 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
403 - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
404 - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
405 - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
406 - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
407 - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
408 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
409 - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
410 - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
411 - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
412 - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
413 - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
414 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
415 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
416 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
417 - ImGuiInputTextCallback -> use ImGuiTextEditCallback
418 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
419 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
420 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
421 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
422 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
423 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
424 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
425 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
426 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
427 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
428 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
429 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
430 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
431 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
432 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
433 - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
434 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
435 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
436 - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
437 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
438 - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
439 - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
440 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
441 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
442 - if you omitted the 'power' parameter (likely!), you are not affected.
443 - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
444 - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
445 see https://github.com/ocornut/imgui/issues/3361 for all details.
446 kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
447 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
448 - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
449 - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
450 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
451 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
452 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
453 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
454 - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
455 - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
456 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
457 - ShowTestWindow() -> use ShowDemoWindow()
458 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
459 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
460 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
461 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
462 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
463 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
464 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
465 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
466 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
467 - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
468 - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
469 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
470 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
471 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
472 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
473 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
474 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
475 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
476 - ImFont::Glyph -> use ImFontGlyph
477 - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.
478 if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.
479 The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).
480 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
481 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
482 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
483 - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.
484 - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have
485 overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.
486 This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.
487 Please reach out if you are affected.
488 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
489 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
490 - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.
491 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
492 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
493 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
494 - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!
495 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
496 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
497 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
498 - 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.
499 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
500 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
501 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
502 - 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.
503 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
504 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
505 - 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.
506 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
507 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
508 - 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).
509 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
510 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
511 - 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.
512 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
513 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
514 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
515 - 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.).
516 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
517 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
518 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
519 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
520 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
521 - 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.
522 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
523 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.
524 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.
525 - 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",
526 consistent with other functions. Kept redirection functions (will obsolete).
527 - 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.
528 - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).
529 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
530 - 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.
531 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
532 - 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.
533 - 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.
534 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
535 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
536 - removed Shutdown() function, as DestroyContext() serve this purpose.
537 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
538 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
539 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
540 - 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.
541 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
542 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
543 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
544 - 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.
545 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
546 - 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
547 - 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.
548 - 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.
549 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
550 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
551 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
552 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
553 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
554 - 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.
555 - 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.
556 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.
557 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
558 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
559 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
560 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
561 - 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.
562 - 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.
563 - 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.
564 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
565 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
566 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
567 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
568 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
569 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
570 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
571 - 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).
572 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
573 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
574 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
575 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
576 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
577 - 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.
578 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
579 - 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.
580 - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).
581 - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).
582 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
583 - 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.
584 - 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.
585 - 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))'
586 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
587 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
588 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
589 - 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().
590 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
591 - 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.
592 - 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.
593 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
594 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
595 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:
596 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }
597 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.
598 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
599 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
600 - 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).
601 - 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.
602 - 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).
603 - 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)
604 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
605 - 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.
606 - 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.
607 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
608 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
609 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
610 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.
611 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!
612 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
613 - 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.
614 - 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
615 - 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.
616 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
617 - 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.
618 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
619 - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
620 - the signature of the io.RenderDrawListsFn handler has changed!
621 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
622 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
623 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
624 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
625 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
626 - 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.
627 - 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!
628 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
629 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
630 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
631 - 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.
632 - 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
633 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
634 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
635 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
636 - 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.
637 - 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.
638 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
639 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
640 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
641 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
642 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
643 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
644 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
645 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
646 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
647 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
648 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
649 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
650 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
651 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
652 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
653 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
654 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
655 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
656 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
657 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
658 you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
659 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
660 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
661 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
662 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
663 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
664 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
665 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
666 - 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)
667 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
668 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
669 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
670 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
671 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
672 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
673
674
675 FREQUENTLY ASKED QUESTIONS (FAQ)
676 ================================
677
678 Read all answers online:
679 https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
680 Read all answers locally (with a text editor or ideally a Markdown viewer):
681 docs/FAQ.md
682 Some answers are copied down here to facilitate searching in code.
683
684 Q&A: Basics
685 ===========
686
687 Q: Where is the documentation?
688 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
689 - Run the examples/ and explore them.
690 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
691 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
692 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
693 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
694 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
695 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
696 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
697 - Your programming IDE is your friend, find the type or function declaration to find comments
698 associated with it.
699
700 Q: What is this library called?
701 Q: Which version should I get?
702 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
703 >> See https://www.dearimgui.org/faq for details.
704
705 Q&A: Integration
706 ================
707
708 Q: How to get started?
709 A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
710
711 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
712 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
713 >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this.
714
715 Q. How can I enable keyboard controls?
716 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
717 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
718 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
719 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
720 >> See https://www.dearimgui.org/faq
721
722 Q&A: Usage
723 ----------
724
725 Q: Why is my widget not reacting when I click on it?
726 Q: How can I have widgets with an empty label?
727 Q: How can I have multiple widgets with the same label?
728 Q: How can I display an image? What is ImTextureID, how does it works?
729 Q: How can I use my own math types instead of ImVec2/ImVec4?
730 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
731 Q: How can I display custom shapes? (using low-level ImDrawList API)
732 >> See https://www.dearimgui.org/faq
733
734 Q&A: Fonts, Text
735 ================
736
737 Q: How should I handle DPI in my application?
738 Q: How can I load a different font than the default?
739 Q: How can I easily use icons in my application?
740 Q: How can I load multiple fonts?
741 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
742 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
743
744 Q&A: Concerns
745 =============
746
747 Q: Who uses Dear ImGui?
748 Q: Can you create elaborate/serious tools with Dear ImGui?
749 Q: Can you reskin the look of Dear ImGui?
750 Q: Why using C++ (as opposed to C)?
751 >> See https://www.dearimgui.org/faq
752
753 Q&A: Community
754 ==============
755
756 Q: How can I help?
757 A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui!
758 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
759 This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project.
760 - Individuals: you can support continued development via PayPal donations. See README.
761 - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt
762 and see how you want to help and can help!
763 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
764 You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
765 But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
766 - 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).
767
768 */
769
770 //-------------------------------------------------------------------------
771 // [SECTION] INCLUDES
772 //-------------------------------------------------------------------------
773
774 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
775 #define _CRT_SECURE_NO_WARNINGS
776 #endif
777
778 #include "imgui.h"
779 #ifndef IMGUI_DISABLE
780
781 #ifndef IMGUI_DEFINE_MATH_OPERATORS
782 #define IMGUI_DEFINE_MATH_OPERATORS
783 #endif
784 #include "imgui_internal.h"
785
786 // System includes
787 #include <ctype.h> // toupper
788 #include <stdio.h> // vsnprintf, sscanf, printf
789 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
790 #include <stddef.h> // intptr_t
791 #else
792 #include <stdint.h> // intptr_t
793 #endif
794
795 // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
796 #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
797 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
798 #endif
799
800 // [Windows] OS specific includes (optional)
801 #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
802 #define IMGUI_DISABLE_WIN32_FUNCTIONS
803 #endif
804 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
805 #ifndef WIN32_LEAN_AND_MEAN
806 #define WIN32_LEAN_AND_MEAN
807 #endif
808 #ifndef NOMINMAX
809 #define NOMINMAX
810 #endif
811 #ifndef __MINGW32__
812 #include <Windows.h> // _wfopen, OpenClipboard
813 #else
814 #include <windows.h>
815 #endif
816 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
817 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
818 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
819 #endif
820 #endif
821
822 // [Apple] OS specific includes
823 #if defined(__APPLE__)
824 #include <TargetConditionals.h>
825 #endif
826
827 // Visual Studio warnings
828 #ifdef _MSC_VER
829 #pragma warning (disable: 4127) // condition expression is constant
830 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
831 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
832 #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
833 #endif
834 #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
835 #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
836 #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
837 #endif
838
839 // Clang/GCC warnings with -Weverything
840 #if defined(__clang__)
841 #if __has_warning("-Wunknown-warning-option")
842 #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
843 #endif
844 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
845 #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
846 #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.
847 #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.
848 #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.
849 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
850 #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
851 #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.
852 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
853 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
854 #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.
855 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
856 #elif defined(__GNUC__)
857 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
858 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
859 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
860 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
861 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
862 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
863 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
864 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
865 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
866 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
867 #endif
868
869 // Debug options
870 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
871 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
872 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
873
874 // 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.
875 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
876 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
877
878 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
879 static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
880 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
881 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
882
883 //-------------------------------------------------------------------------
884 // [SECTION] FORWARD DECLARATIONS
885 //-------------------------------------------------------------------------
886
887 static void SetCurrentWindow(ImGuiWindow* window);
888 static void FindHoveredWindow();
889 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
890 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
891
892 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
893 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
894
895 // Settings
896 static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
897 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
898 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
899 static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
900 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
901
902 // Platform Dependents default implementation for IO functions
903 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
904 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
905 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
906
907 namespace ImGui
908 {
909 // Navigation
910 static void NavUpdate();
911 static void NavUpdateWindowing();
912 static void NavUpdateWindowingOverlay();
913 static void NavUpdateInitResult();
914 static void NavUpdateCancelRequest();
915 static void NavUpdateCreateMoveRequest();
916 static float NavUpdatePageUpPageDown();
917 static inline void NavUpdateAnyRequestFlag();
918 static void NavEndFrame();
919 static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand);
920 static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
921 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
922 static ImVec2 NavCalcPreferredRefPos();
923 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
924 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
925 static void NavRestoreLayer(ImGuiNavLayer layer);
926 static int FindWindowFocusIndex(ImGuiWindow* window);
927
928 // Error Checking
929 static void ErrorCheckNewFrameSanityChecks();
930 static void ErrorCheckEndFrameSanityChecks();
931
932 // Misc
933 static void UpdateSettings();
934 static void UpdateMouseInputs();
935 static void UpdateMouseWheel();
936 static void UpdateTabFocus();
937 static void UpdateDebugToolItemPicker();
938 static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
939 static void RenderWindowOuterBorders(ImGuiWindow* window);
940 static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
941 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
942
943 // Viewports
944 static void UpdateViewportsNewFrame();
945
946 }
947
948 //-----------------------------------------------------------------------------
949 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
950 //-----------------------------------------------------------------------------
951
952 // DLL users:
953 // - Heaps and globals are not shared across DLL boundaries!
954 // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
955 // - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
956 // - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
957 // - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
958
959 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
960 // - ImGui::CreateContext() will automatically set this pointer if it is NULL.
961 // Change to a different context by calling ImGui::SetCurrentContext().
962 // - Important: Dear ImGui functions are not thread-safe because of this pointer.
963 // If you want thread-safety to allow N threads to access N different contexts:
964 // - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
965 // struct ImGuiContext;
966 // extern thread_local ImGuiContext* MyImGuiTLS;
967 // #define GImGui MyImGuiTLS
968 // And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
969 // - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
970 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
971 // - DLL users: read comments above.
972 #ifndef GImGui
973 ImGuiContext* GImGui = NULL;
974 #endif
975
976 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
977 // - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
978 // - DLL users: read comments above.
979 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)980 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)981 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
982 #else
MallocWrapper(size_t size,void * user_data)983 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)984 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
985 #endif
986 static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
987 static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
988 static void* GImAllocatorUserData = NULL;
989
990 //-----------------------------------------------------------------------------
991 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
992 //-----------------------------------------------------------------------------
993
ImGuiStyle()994 ImGuiStyle::ImGuiStyle()
995 {
996 Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui.
997 DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
998 WindowPadding = ImVec2(8,8); // Padding within a window
999 WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
1000 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1001 WindowMinSize = ImVec2(32,32); // Minimum window size
1002 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1003 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1004 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1005 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1006 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1007 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1008 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1009 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1010 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1011 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1012 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1013 CellPadding = ImVec2(4,2); // Padding within a table cell
1014 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!
1015 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1016 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1017 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1018 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1019 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
1020 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1021 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1022 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1023 TabBorderSize = 0.0f; // Thickness of border around tabs.
1024 TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.
1025 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1026 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1027 SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
1028 DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
1029 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.
1030 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1031 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1032 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
1033 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1034 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.
1035 CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
1036
1037 // Default theme
1038 ImGui::StyleColorsDark(this);
1039 }
1040
1041 // 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.
1042 // 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.
ScaleAllSizes(float scale_factor)1043 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1044 {
1045 WindowPadding = ImFloor(WindowPadding * scale_factor);
1046 WindowRounding = ImFloor(WindowRounding * scale_factor);
1047 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1048 ChildRounding = ImFloor(ChildRounding * scale_factor);
1049 PopupRounding = ImFloor(PopupRounding * scale_factor);
1050 FramePadding = ImFloor(FramePadding * scale_factor);
1051 FrameRounding = ImFloor(FrameRounding * scale_factor);
1052 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1053 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1054 CellPadding = ImFloor(CellPadding * scale_factor);
1055 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1056 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1057 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1058 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1059 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1060 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1061 GrabRounding = ImFloor(GrabRounding * scale_factor);
1062 LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1063 TabRounding = ImFloor(TabRounding * scale_factor);
1064 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1065 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1066 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1067 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1068 }
1069
ImGuiIO()1070 ImGuiIO::ImGuiIO()
1071 {
1072 // Most fields are initialized with zero
1073 memset(this, 0, sizeof(*this));
1074 IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here.
1075
1076 // Settings
1077 ConfigFlags = ImGuiConfigFlags_None;
1078 BackendFlags = ImGuiBackendFlags_None;
1079 DisplaySize = ImVec2(-1.0f, -1.0f);
1080 DeltaTime = 1.0f / 60.0f;
1081 IniSavingRate = 5.0f;
1082 IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
1083 LogFilename = "imgui_log.txt";
1084 MouseDoubleClickTime = 0.30f;
1085 MouseDoubleClickMaxDist = 6.0f;
1086 for (int i = 0; i < ImGuiKey_COUNT; i++)
1087 KeyMap[i] = -1;
1088 KeyRepeatDelay = 0.275f;
1089 KeyRepeatRate = 0.050f;
1090 UserData = NULL;
1091
1092 Fonts = NULL;
1093 FontGlobalScale = 1.0f;
1094 FontDefault = NULL;
1095 FontAllowUserScaling = false;
1096 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1097
1098 // Miscellaneous options
1099 MouseDrawCursor = false;
1100 #ifdef __APPLE__
1101 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1102 #else
1103 ConfigMacOSXBehaviors = false;
1104 #endif
1105 ConfigInputTextCursorBlink = true;
1106 ConfigWindowsResizeFromEdges = true;
1107 ConfigWindowsMoveFromTitleBarOnly = false;
1108 ConfigMemoryCompactTimer = 60.0f;
1109
1110 // Platform Functions
1111 BackendPlatformName = BackendRendererName = NULL;
1112 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1113 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1114 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1115 ClipboardUserData = NULL;
1116 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1117 ImeWindowHandle = NULL;
1118
1119 // Input (NB: we already have memset zero the entire structure!)
1120 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1121 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1122 MouseDragThreshold = 6.0f;
1123 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1124 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1125 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1126 }
1127
1128 // Pass in translated ASCII characters for text input.
1129 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1130 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1131 void ImGuiIO::AddInputCharacter(unsigned int c)
1132 {
1133 if (c != 0)
1134 InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1135 }
1136
1137 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1138 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1139 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1140 {
1141 if (c == 0 && InputQueueSurrogate == 0)
1142 return;
1143
1144 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1145 {
1146 if (InputQueueSurrogate != 0)
1147 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1148 InputQueueSurrogate = c;
1149 return;
1150 }
1151
1152 ImWchar cp = c;
1153 if (InputQueueSurrogate != 0)
1154 {
1155 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1156 {
1157 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1158 }
1159 else
1160 {
1161 #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1162 cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1163 #else
1164 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1165 #endif
1166 }
1167
1168 InputQueueSurrogate = 0;
1169 }
1170 InputQueueCharacters.push_back(cp);
1171 }
1172
AddInputCharactersUTF8(const char * utf8_chars)1173 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1174 {
1175 while (*utf8_chars != 0)
1176 {
1177 unsigned int c = 0;
1178 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1179 if (c != 0)
1180 InputQueueCharacters.push_back((ImWchar)c);
1181 }
1182 }
1183
ClearInputCharacters()1184 void ImGuiIO::ClearInputCharacters()
1185 {
1186 InputQueueCharacters.resize(0);
1187 }
1188
AddFocusEvent(bool focused)1189 void ImGuiIO::AddFocusEvent(bool focused)
1190 {
1191 if (focused)
1192 return;
1193
1194 // Clear buttons state when focus is lost
1195 // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle)
1196 memset(KeysDown, 0, sizeof(KeysDown));
1197 for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++)
1198 KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f;
1199 KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1200 KeyMods = KeyModsPrev = ImGuiKeyModFlags_None;
1201 for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++)
1202 NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f;
1203 }
1204
1205 //-----------------------------------------------------------------------------
1206 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1207 //-----------------------------------------------------------------------------
1208
ImBezierCubicClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1209 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1210 {
1211 IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1212 ImVec2 p_last = p1;
1213 ImVec2 p_closest;
1214 float p_closest_dist2 = FLT_MAX;
1215 float t_step = 1.0f / (float)num_segments;
1216 for (int i_step = 1; i_step <= num_segments; i_step++)
1217 {
1218 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1219 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1220 float dist2 = ImLengthSqr(p - p_line);
1221 if (dist2 < p_closest_dist2)
1222 {
1223 p_closest = p_line;
1224 p_closest_dist2 = dist2;
1225 }
1226 p_last = p_current;
1227 }
1228 return p_closest;
1229 }
1230
1231 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
ImBezierCubicClosestPointCasteljauStep(const ImVec2 & p,ImVec2 & p_closest,ImVec2 & p_last,float & p_closest_dist2,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float tess_tol,int level)1232 static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1233 {
1234 float dx = x4 - x1;
1235 float dy = y4 - y1;
1236 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1237 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1238 d2 = (d2 >= 0) ? d2 : -d2;
1239 d3 = (d3 >= 0) ? d3 : -d3;
1240 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1241 {
1242 ImVec2 p_current(x4, y4);
1243 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1244 float dist2 = ImLengthSqr(p - p_line);
1245 if (dist2 < p_closest_dist2)
1246 {
1247 p_closest = p_line;
1248 p_closest_dist2 = dist2;
1249 }
1250 p_last = p_current;
1251 }
1252 else if (level < 10)
1253 {
1254 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1255 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1256 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1257 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1258 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1259 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1260 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1261 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1262 }
1263 }
1264
1265 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1266 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierCubicClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1267 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1268 {
1269 IM_ASSERT(tess_tol > 0.0f);
1270 ImVec2 p_last = p1;
1271 ImVec2 p_closest;
1272 float p_closest_dist2 = FLT_MAX;
1273 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1274 return p_closest;
1275 }
1276
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1277 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1278 {
1279 ImVec2 ap = p - a;
1280 ImVec2 ab_dir = b - a;
1281 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1282 if (dot < 0.0f)
1283 return a;
1284 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1285 if (dot > ab_len_sqr)
1286 return b;
1287 return a + ab_dir * dot / ab_len_sqr;
1288 }
1289
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1290 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1291 {
1292 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1293 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1294 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1295 return ((b1 == b2) && (b2 == b3));
1296 }
1297
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1298 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1299 {
1300 ImVec2 v0 = b - a;
1301 ImVec2 v1 = c - a;
1302 ImVec2 v2 = p - a;
1303 const float denom = v0.x * v1.y - v1.x * v0.y;
1304 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1305 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1306 out_u = 1.0f - out_v - out_w;
1307 }
1308
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1309 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1310 {
1311 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1312 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1313 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1314 float dist2_ab = ImLengthSqr(p - proj_ab);
1315 float dist2_bc = ImLengthSqr(p - proj_bc);
1316 float dist2_ca = ImLengthSqr(p - proj_ca);
1317 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1318 if (m == dist2_ab)
1319 return proj_ab;
1320 if (m == dist2_bc)
1321 return proj_bc;
1322 return proj_ca;
1323 }
1324
1325 //-----------------------------------------------------------------------------
1326 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1327 //-----------------------------------------------------------------------------
1328
1329 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
ImStricmp(const char * str1,const char * str2)1330 int ImStricmp(const char* str1, const char* str2)
1331 {
1332 int d;
1333 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1334 return d;
1335 }
1336
ImStrnicmp(const char * str1,const char * str2,size_t count)1337 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1338 {
1339 int d = 0;
1340 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1341 return d;
1342 }
1343
ImStrncpy(char * dst,const char * src,size_t count)1344 void ImStrncpy(char* dst, const char* src, size_t count)
1345 {
1346 if (count < 1)
1347 return;
1348 if (count > 1)
1349 strncpy(dst, src, count - 1);
1350 dst[count - 1] = 0;
1351 }
1352
ImStrdup(const char * str)1353 char* ImStrdup(const char* str)
1354 {
1355 size_t len = strlen(str);
1356 void* buf = IM_ALLOC(len + 1);
1357 return (char*)memcpy(buf, (const void*)str, len + 1);
1358 }
1359
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1360 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1361 {
1362 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1363 size_t src_size = strlen(src) + 1;
1364 if (dst_buf_size < src_size)
1365 {
1366 IM_FREE(dst);
1367 dst = (char*)IM_ALLOC(src_size);
1368 if (p_dst_size)
1369 *p_dst_size = src_size;
1370 }
1371 return (char*)memcpy(dst, (const void*)src, src_size);
1372 }
1373
ImStrchrRange(const char * str,const char * str_end,char c)1374 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1375 {
1376 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1377 return p;
1378 }
1379
ImStrlenW(const ImWchar * str)1380 int ImStrlenW(const ImWchar* str)
1381 {
1382 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1383 int n = 0;
1384 while (*str++) n++;
1385 return n;
1386 }
1387
1388 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1389 const char* ImStreolRange(const char* str, const char* str_end)
1390 {
1391 const char* p = (const char*)memchr(str, '\n', str_end - str);
1392 return p ? p : str_end;
1393 }
1394
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1395 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1396 {
1397 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1398 buf_mid_line--;
1399 return buf_mid_line;
1400 }
1401
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1402 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1403 {
1404 if (!needle_end)
1405 needle_end = needle + strlen(needle);
1406
1407 const char un0 = (char)toupper(*needle);
1408 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1409 {
1410 if (toupper(*haystack) == un0)
1411 {
1412 const char* b = needle + 1;
1413 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1414 if (toupper(*a) != toupper(*b))
1415 break;
1416 if (b == needle_end)
1417 return haystack;
1418 }
1419 haystack++;
1420 }
1421 return NULL;
1422 }
1423
1424 // 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.
ImStrTrimBlanks(char * buf)1425 void ImStrTrimBlanks(char* buf)
1426 {
1427 char* p = buf;
1428 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1429 p++;
1430 char* p_start = p;
1431 while (*p != 0) // Find end of string
1432 p++;
1433 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1434 p--;
1435 if (p_start != buf) // Copy memory if we had leading blanks
1436 memmove(buf, p_start, p - p_start);
1437 buf[p - p_start] = 0; // Zero terminate
1438 }
1439
ImStrSkipBlank(const char * str)1440 const char* ImStrSkipBlank(const char* str)
1441 {
1442 while (str[0] == ' ' || str[0] == '\t')
1443 str++;
1444 return str;
1445 }
1446
1447 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1448 // 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.
1449 // B) When buf==NULL vsnprintf() will return the output size.
1450 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1451
1452 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1453 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1454 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1455 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1456 #ifdef IMGUI_USE_STB_SPRINTF
1457 #define STB_SPRINTF_IMPLEMENTATION
1458 #include "stb_sprintf.h"
1459 #endif
1460
1461 #if defined(_MSC_VER) && !defined(vsnprintf)
1462 #define vsnprintf _vsnprintf
1463 #endif
1464
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1465 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1466 {
1467 va_list args;
1468 va_start(args, fmt);
1469 #ifdef IMGUI_USE_STB_SPRINTF
1470 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1471 #else
1472 int w = vsnprintf(buf, buf_size, fmt, args);
1473 #endif
1474 va_end(args);
1475 if (buf == NULL)
1476 return w;
1477 if (w == -1 || w >= (int)buf_size)
1478 w = (int)buf_size - 1;
1479 buf[w] = 0;
1480 return w;
1481 }
1482
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1483 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1484 {
1485 #ifdef IMGUI_USE_STB_SPRINTF
1486 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1487 #else
1488 int w = vsnprintf(buf, buf_size, fmt, args);
1489 #endif
1490 if (buf == NULL)
1491 return w;
1492 if (w == -1 || w >= (int)buf_size)
1493 w = (int)buf_size - 1;
1494 buf[w] = 0;
1495 return w;
1496 }
1497 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1498
1499 // CRC32 needs a 1KB lookup table (not cache friendly)
1500 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1501 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1502 static const ImU32 GCrc32LookupTable[256] =
1503 {
1504 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1505 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1506 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1507 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1508 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1509 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1510 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1511 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1512 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1513 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1514 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1515 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1516 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1517 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1518 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1519 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1520 };
1521
1522 // Known size hash
1523 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1524 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashData(const void * data_p,size_t data_size,ImU32 seed)1525 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1526 {
1527 ImU32 crc = ~seed;
1528 const unsigned char* data = (const unsigned char*)data_p;
1529 const ImU32* crc32_lut = GCrc32LookupTable;
1530 while (data_size-- != 0)
1531 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1532 return ~crc;
1533 }
1534
1535 // Zero-terminated string hash, with support for ### to reset back to seed value
1536 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1537 // Because this syntax is rarely used we are optimizing for the common case.
1538 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1539 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1540 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashStr(const char * data_p,size_t data_size,ImU32 seed)1541 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1542 {
1543 seed = ~seed;
1544 ImU32 crc = seed;
1545 const unsigned char* data = (const unsigned char*)data_p;
1546 const ImU32* crc32_lut = GCrc32LookupTable;
1547 if (data_size != 0)
1548 {
1549 while (data_size-- != 0)
1550 {
1551 unsigned char c = *data++;
1552 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1553 crc = seed;
1554 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1555 }
1556 }
1557 else
1558 {
1559 while (unsigned char c = *data++)
1560 {
1561 if (c == '#' && data[0] == '#' && data[1] == '#')
1562 crc = seed;
1563 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1564 }
1565 }
1566 return ~crc;
1567 }
1568
1569 //-----------------------------------------------------------------------------
1570 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1571 //-----------------------------------------------------------------------------
1572
1573 // Default file functions
1574 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1575
ImFileOpen(const char * filename,const char * mode)1576 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1577 {
1578 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1579 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1580 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1581 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1582 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1583 ImVector<ImWchar> buf;
1584 buf.resize(filename_wsize + mode_wsize);
1585 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1586 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1587 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1588 #else
1589 return fopen(filename, mode);
1590 #endif
1591 }
1592
1593 // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
ImFileClose(ImFileHandle f)1594 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1595 ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
ImFileRead(void * data,ImU64 sz,ImU64 count,ImFileHandle f)1596 ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
ImFileWrite(const void * data,ImU64 sz,ImU64 count,ImFileHandle f)1597 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
1598 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1599
1600 // Helper: Load file content into memory
1601 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1602 // This can't really be used with "rt" because fseek size won't match read size.
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1603 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1604 {
1605 IM_ASSERT(filename && mode);
1606 if (out_file_size)
1607 *out_file_size = 0;
1608
1609 ImFileHandle f;
1610 if ((f = ImFileOpen(filename, mode)) == NULL)
1611 return NULL;
1612
1613 size_t file_size = (size_t)ImFileGetSize(f);
1614 if (file_size == (size_t)-1)
1615 {
1616 ImFileClose(f);
1617 return NULL;
1618 }
1619
1620 void* file_data = IM_ALLOC(file_size + padding_bytes);
1621 if (file_data == NULL)
1622 {
1623 ImFileClose(f);
1624 return NULL;
1625 }
1626 if (ImFileRead(file_data, 1, file_size, f) != file_size)
1627 {
1628 ImFileClose(f);
1629 IM_FREE(file_data);
1630 return NULL;
1631 }
1632 if (padding_bytes > 0)
1633 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1634
1635 ImFileClose(f);
1636 if (out_file_size)
1637 *out_file_size = file_size;
1638
1639 return file_data;
1640 }
1641
1642 //-----------------------------------------------------------------------------
1643 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1644 //-----------------------------------------------------------------------------
1645
1646 // Convert UTF-8 to 32-bit character, process single character input.
1647 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1648 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1649 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1650 {
1651 static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
1652 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1653 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1654 static const int shiftc[] = { 0, 18, 12, 6, 0 };
1655 static const int shifte[] = { 0, 6, 4, 2, 0 };
1656 int len = lengths[*(const unsigned char*)in_text >> 3];
1657 int wanted = len + !len;
1658
1659 if (in_text_end == NULL)
1660 in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1661
1662 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1663 // so it is fast even with excessive branching.
1664 unsigned char s[4];
1665 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1666 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1667 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1668 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1669
1670 // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1671 *out_char = (uint32_t)(s[0] & masks[len]) << 18;
1672 *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1673 *out_char |= (uint32_t)(s[2] & 0x3f) << 6;
1674 *out_char |= (uint32_t)(s[3] & 0x3f) << 0;
1675 *out_char >>= shiftc[len];
1676
1677 // Accumulate the various error conditions.
1678 int e = 0;
1679 e = (*out_char < mins[len]) << 6; // non-canonical encoding
1680 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
1681 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range?
1682 e |= (s[1] & 0xc0) >> 2;
1683 e |= (s[2] & 0xc0) >> 4;
1684 e |= (s[3] ) >> 6;
1685 e ^= 0x2a; // top two bits of each tail byte correct?
1686 e >>= shifte[len];
1687
1688 if (e)
1689 {
1690 // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1691 // One byte is consumed in case of invalid first byte of in_text.
1692 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1693 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1694 wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1695 *out_char = IM_UNICODE_CODEPOINT_INVALID;
1696 }
1697
1698 return wanted;
1699 }
1700
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1701 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1702 {
1703 ImWchar* buf_out = buf;
1704 ImWchar* buf_end = buf + buf_size;
1705 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1706 {
1707 unsigned int c;
1708 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1709 if (c == 0)
1710 break;
1711 *buf_out++ = (ImWchar)c;
1712 }
1713 *buf_out = 0;
1714 if (in_text_remaining)
1715 *in_text_remaining = in_text;
1716 return (int)(buf_out - buf);
1717 }
1718
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1719 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1720 {
1721 int char_count = 0;
1722 while ((!in_text_end || in_text < in_text_end) && *in_text)
1723 {
1724 unsigned int c;
1725 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1726 if (c == 0)
1727 break;
1728 char_count++;
1729 }
1730 return char_count;
1731 }
1732
1733 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8_inline(char * buf,int buf_size,unsigned int c)1734 static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
1735 {
1736 if (c < 0x80)
1737 {
1738 buf[0] = (char)c;
1739 return 1;
1740 }
1741 if (c < 0x800)
1742 {
1743 if (buf_size < 2) return 0;
1744 buf[0] = (char)(0xc0 + (c >> 6));
1745 buf[1] = (char)(0x80 + (c & 0x3f));
1746 return 2;
1747 }
1748 if (c < 0x10000)
1749 {
1750 if (buf_size < 3) return 0;
1751 buf[0] = (char)(0xe0 + (c >> 12));
1752 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1753 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1754 return 3;
1755 }
1756 if (c <= 0x10FFFF)
1757 {
1758 if (buf_size < 4) return 0;
1759 buf[0] = (char)(0xf0 + (c >> 18));
1760 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1761 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1762 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1763 return 4;
1764 }
1765 // Invalid code point, the max unicode is 0x10FFFF
1766 return 0;
1767 }
1768
ImTextCharToUtf8(char out_buf[5],unsigned int c)1769 const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
1770 {
1771 int count = ImTextCharToUtf8_inline(out_buf, 5, c);
1772 out_buf[count] = 0;
1773 return out_buf;
1774 }
1775
1776 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1777 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1778 {
1779 unsigned int unused = 0;
1780 return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1781 }
1782
ImTextCountUtf8BytesFromChar(unsigned int c)1783 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1784 {
1785 if (c < 0x80) return 1;
1786 if (c < 0x800) return 2;
1787 if (c < 0x10000) return 3;
1788 if (c <= 0x10FFFF) return 4;
1789 return 3;
1790 }
1791
ImTextStrToUtf8(char * out_buf,int out_buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1792 int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1793 {
1794 char* buf_p = out_buf;
1795 const char* buf_end = out_buf + out_buf_size;
1796 while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1797 {
1798 unsigned int c = (unsigned int)(*in_text++);
1799 if (c < 0x80)
1800 *buf_p++ = (char)c;
1801 else
1802 buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
1803 }
1804 *buf_p = 0;
1805 return (int)(buf_p - out_buf);
1806 }
1807
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1808 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1809 {
1810 int bytes_count = 0;
1811 while ((!in_text_end || in_text < in_text_end) && *in_text)
1812 {
1813 unsigned int c = (unsigned int)(*in_text++);
1814 if (c < 0x80)
1815 bytes_count++;
1816 else
1817 bytes_count += ImTextCountUtf8BytesFromChar(c);
1818 }
1819 return bytes_count;
1820 }
1821
1822 //-----------------------------------------------------------------------------
1823 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1824 // Note: The Convert functions are early design which are not consistent with other API.
1825 //-----------------------------------------------------------------------------
1826
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1827 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1828 {
1829 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1830 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1831 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1832 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1833 return IM_COL32(r, g, b, 0xFF);
1834 }
1835
ColorConvertU32ToFloat4(ImU32 in)1836 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1837 {
1838 float s = 1.0f / 255.0f;
1839 return ImVec4(
1840 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1841 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1842 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1843 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1844 }
1845
ColorConvertFloat4ToU32(const ImVec4 & in)1846 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1847 {
1848 ImU32 out;
1849 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1850 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1851 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1852 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1853 return out;
1854 }
1855
1856 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1857 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1858 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1859 {
1860 float K = 0.f;
1861 if (g < b)
1862 {
1863 ImSwap(g, b);
1864 K = -1.f;
1865 }
1866 if (r < g)
1867 {
1868 ImSwap(r, g);
1869 K = -2.f / 6.f - K;
1870 }
1871
1872 const float chroma = r - (g < b ? g : b);
1873 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1874 out_s = chroma / (r + 1e-20f);
1875 out_v = r;
1876 }
1877
1878 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1879 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1880 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1881 {
1882 if (s == 0.0f)
1883 {
1884 // gray
1885 out_r = out_g = out_b = v;
1886 return;
1887 }
1888
1889 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1890 int i = (int)h;
1891 float f = h - (float)i;
1892 float p = v * (1.0f - s);
1893 float q = v * (1.0f - s * f);
1894 float t = v * (1.0f - s * (1.0f - f));
1895
1896 switch (i)
1897 {
1898 case 0: out_r = v; out_g = t; out_b = p; break;
1899 case 1: out_r = q; out_g = v; out_b = p; break;
1900 case 2: out_r = p; out_g = v; out_b = t; break;
1901 case 3: out_r = p; out_g = q; out_b = v; break;
1902 case 4: out_r = t; out_g = p; out_b = v; break;
1903 case 5: default: out_r = v; out_g = p; out_b = q; break;
1904 }
1905 }
1906
1907 //-----------------------------------------------------------------------------
1908 // [SECTION] ImGuiStorage
1909 // Helper: Key->value storage
1910 //-----------------------------------------------------------------------------
1911
1912 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1913 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1914 {
1915 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1916 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1917 size_t count = (size_t)(last - first);
1918 while (count > 0)
1919 {
1920 size_t count2 = count >> 1;
1921 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1922 if (mid->key < key)
1923 {
1924 first = ++mid;
1925 count -= count2 + 1;
1926 }
1927 else
1928 {
1929 count = count2;
1930 }
1931 }
1932 return first;
1933 }
1934
1935 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1936 void ImGuiStorage::BuildSortByKey()
1937 {
1938 struct StaticFunc
1939 {
1940 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1941 {
1942 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1943 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1944 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1945 return 0;
1946 }
1947 };
1948 if (Data.Size > 1)
1949 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1950 }
1951
GetInt(ImGuiID key,int default_val) const1952 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1953 {
1954 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1955 if (it == Data.end() || it->key != key)
1956 return default_val;
1957 return it->val_i;
1958 }
1959
GetBool(ImGuiID key,bool default_val) const1960 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1961 {
1962 return GetInt(key, default_val ? 1 : 0) != 0;
1963 }
1964
GetFloat(ImGuiID key,float default_val) const1965 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1966 {
1967 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1968 if (it == Data.end() || it->key != key)
1969 return default_val;
1970 return it->val_f;
1971 }
1972
GetVoidPtr(ImGuiID key) const1973 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1974 {
1975 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1976 if (it == Data.end() || it->key != key)
1977 return NULL;
1978 return it->val_p;
1979 }
1980
1981 // 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.
GetIntRef(ImGuiID key,int default_val)1982 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1983 {
1984 ImGuiStoragePair* it = LowerBound(Data, key);
1985 if (it == Data.end() || it->key != key)
1986 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1987 return &it->val_i;
1988 }
1989
GetBoolRef(ImGuiID key,bool default_val)1990 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1991 {
1992 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1993 }
1994
GetFloatRef(ImGuiID key,float default_val)1995 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1996 {
1997 ImGuiStoragePair* it = LowerBound(Data, key);
1998 if (it == Data.end() || it->key != key)
1999 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2000 return &it->val_f;
2001 }
2002
GetVoidPtrRef(ImGuiID key,void * default_val)2003 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2004 {
2005 ImGuiStoragePair* it = LowerBound(Data, key);
2006 if (it == Data.end() || it->key != key)
2007 it = Data.insert(it, ImGuiStoragePair(key, default_val));
2008 return &it->val_p;
2009 }
2010
2011 // 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)
SetInt(ImGuiID key,int val)2012 void ImGuiStorage::SetInt(ImGuiID key, int val)
2013 {
2014 ImGuiStoragePair* it = LowerBound(Data, key);
2015 if (it == Data.end() || it->key != key)
2016 {
2017 Data.insert(it, ImGuiStoragePair(key, val));
2018 return;
2019 }
2020 it->val_i = val;
2021 }
2022
SetBool(ImGuiID key,bool val)2023 void ImGuiStorage::SetBool(ImGuiID key, bool val)
2024 {
2025 SetInt(key, val ? 1 : 0);
2026 }
2027
SetFloat(ImGuiID key,float val)2028 void ImGuiStorage::SetFloat(ImGuiID key, float val)
2029 {
2030 ImGuiStoragePair* it = LowerBound(Data, key);
2031 if (it == Data.end() || it->key != key)
2032 {
2033 Data.insert(it, ImGuiStoragePair(key, val));
2034 return;
2035 }
2036 it->val_f = val;
2037 }
2038
SetVoidPtr(ImGuiID key,void * val)2039 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2040 {
2041 ImGuiStoragePair* it = LowerBound(Data, key);
2042 if (it == Data.end() || it->key != key)
2043 {
2044 Data.insert(it, ImGuiStoragePair(key, val));
2045 return;
2046 }
2047 it->val_p = val;
2048 }
2049
SetAllInt(int v)2050 void ImGuiStorage::SetAllInt(int v)
2051 {
2052 for (int i = 0; i < Data.Size; i++)
2053 Data[i].val_i = v;
2054 }
2055
2056 //-----------------------------------------------------------------------------
2057 // [SECTION] ImGuiTextFilter
2058 //-----------------------------------------------------------------------------
2059
2060 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)2061 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2062 {
2063 if (default_filter)
2064 {
2065 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2066 Build();
2067 }
2068 else
2069 {
2070 InputBuf[0] = 0;
2071 CountGrep = 0;
2072 }
2073 }
2074
Draw(const char * label,float width)2075 bool ImGuiTextFilter::Draw(const char* label, float width)
2076 {
2077 if (width != 0.0f)
2078 ImGui::SetNextItemWidth(width);
2079 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2080 if (value_changed)
2081 Build();
2082 return value_changed;
2083 }
2084
split(char separator,ImVector<ImGuiTextRange> * out) const2085 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2086 {
2087 out->resize(0);
2088 const char* wb = b;
2089 const char* we = wb;
2090 while (we < e)
2091 {
2092 if (*we == separator)
2093 {
2094 out->push_back(ImGuiTextRange(wb, we));
2095 wb = we + 1;
2096 }
2097 we++;
2098 }
2099 if (wb != we)
2100 out->push_back(ImGuiTextRange(wb, we));
2101 }
2102
Build()2103 void ImGuiTextFilter::Build()
2104 {
2105 Filters.resize(0);
2106 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2107 input_range.split(',', &Filters);
2108
2109 CountGrep = 0;
2110 for (int i = 0; i != Filters.Size; i++)
2111 {
2112 ImGuiTextRange& f = Filters[i];
2113 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2114 f.b++;
2115 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2116 f.e--;
2117 if (f.empty())
2118 continue;
2119 if (Filters[i].b[0] != '-')
2120 CountGrep += 1;
2121 }
2122 }
2123
PassFilter(const char * text,const char * text_end) const2124 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2125 {
2126 if (Filters.empty())
2127 return true;
2128
2129 if (text == NULL)
2130 text = "";
2131
2132 for (int i = 0; i != Filters.Size; i++)
2133 {
2134 const ImGuiTextRange& f = Filters[i];
2135 if (f.empty())
2136 continue;
2137 if (f.b[0] == '-')
2138 {
2139 // Subtract
2140 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2141 return false;
2142 }
2143 else
2144 {
2145 // Grep
2146 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2147 return true;
2148 }
2149 }
2150
2151 // Implicit * grep
2152 if (CountGrep == 0)
2153 return true;
2154
2155 return false;
2156 }
2157
2158 //-----------------------------------------------------------------------------
2159 // [SECTION] ImGuiTextBuffer
2160 //-----------------------------------------------------------------------------
2161
2162 // On some platform vsnprintf() takes va_list by reference and modifies it.
2163 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2164 #ifndef va_copy
2165 #if defined(__GNUC__) || defined(__clang__)
2166 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2167 #else
2168 #define va_copy(dest, src) (dest = src)
2169 #endif
2170 #endif
2171
2172 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2173
append(const char * str,const char * str_end)2174 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2175 {
2176 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2177
2178 // Add zero-terminator the first time
2179 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2180 const int needed_sz = write_off + len;
2181 if (write_off + len >= Buf.Capacity)
2182 {
2183 int new_capacity = Buf.Capacity * 2;
2184 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2185 }
2186
2187 Buf.resize(needed_sz);
2188 memcpy(&Buf[write_off - 1], str, (size_t)len);
2189 Buf[write_off - 1 + len] = 0;
2190 }
2191
appendf(const char * fmt,...)2192 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2193 {
2194 va_list args;
2195 va_start(args, fmt);
2196 appendfv(fmt, args);
2197 va_end(args);
2198 }
2199
2200 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2201 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2202 {
2203 va_list args_copy;
2204 va_copy(args_copy, args);
2205
2206 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2207 if (len <= 0)
2208 {
2209 va_end(args_copy);
2210 return;
2211 }
2212
2213 // Add zero-terminator the first time
2214 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2215 const int needed_sz = write_off + len;
2216 if (write_off + len >= Buf.Capacity)
2217 {
2218 int new_capacity = Buf.Capacity * 2;
2219 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2220 }
2221
2222 Buf.resize(needed_sz);
2223 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2224 va_end(args_copy);
2225 }
2226
2227 //-----------------------------------------------------------------------------
2228 // [SECTION] ImGuiListClipper
2229 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2230 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2231 //-----------------------------------------------------------------------------
2232
2233 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2234 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
GetSkipItemForListClipping()2235 static bool GetSkipItemForListClipping()
2236 {
2237 ImGuiContext& g = *GImGui;
2238 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2239 }
2240
2241 // Helper to calculate coarse clipping of large list of evenly sized items.
2242 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2243 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)2244 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2245 {
2246 ImGuiContext& g = *GImGui;
2247 ImGuiWindow* window = g.CurrentWindow;
2248 if (g.LogEnabled)
2249 {
2250 // If logging is active, do not perform any clipping
2251 *out_items_display_start = 0;
2252 *out_items_display_end = items_count;
2253 return;
2254 }
2255 if (GetSkipItemForListClipping())
2256 {
2257 *out_items_display_start = *out_items_display_end = 0;
2258 return;
2259 }
2260
2261 // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect
2262 ImRect unclipped_rect = window->ClipRect;
2263 if (g.NavMoveScoringItems)
2264 unclipped_rect.Add(g.NavScoringRect);
2265 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2266 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); // Could store and use NavJustMovedToRectRel
2267
2268 const ImVec2 pos = window->DC.CursorPos;
2269 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2270 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2271
2272 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2273 if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up)
2274 start--;
2275 if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down)
2276 end++;
2277
2278 start = ImClamp(start, 0, items_count);
2279 end = ImClamp(end + 1, start, items_count);
2280 *out_items_display_start = start;
2281 *out_items_display_end = end;
2282 }
2283
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2284 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2285 {
2286 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2287 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2288 // The clipper should probably have a 4th step to display the last item in a regular manner.
2289 ImGuiContext& g = *GImGui;
2290 ImGuiWindow* window = g.CurrentWindow;
2291 float off_y = pos_y - window->DC.CursorPos.y;
2292 window->DC.CursorPos.y = pos_y;
2293 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2294 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.
2295 window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
2296 if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2297 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2298 if (ImGuiTable* table = g.CurrentTable)
2299 {
2300 if (table->IsInsideRow)
2301 ImGui::TableEndRow(table);
2302 table->RowPosY2 = window->DC.CursorPos.y;
2303 const int row_increase = (int)((off_y / line_height) + 0.5f);
2304 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2305 table->RowBgColorCounter += row_increase;
2306 }
2307 }
2308
ImGuiListClipper()2309 ImGuiListClipper::ImGuiListClipper()
2310 {
2311 memset(this, 0, sizeof(*this));
2312 ItemsCount = -1;
2313 }
2314
~ImGuiListClipper()2315 ImGuiListClipper::~ImGuiListClipper()
2316 {
2317 IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2318 }
2319
2320 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2321 // Use case B: Begin() called from constructor with items_height>0
2322 // 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.
Begin(int items_count,float items_height)2323 void ImGuiListClipper::Begin(int items_count, float items_height)
2324 {
2325 ImGuiContext& g = *GImGui;
2326 ImGuiWindow* window = g.CurrentWindow;
2327
2328 if (ImGuiTable* table = g.CurrentTable)
2329 if (table->IsInsideRow)
2330 ImGui::TableEndRow(table);
2331
2332 StartPosY = window->DC.CursorPos.y;
2333 ItemsHeight = items_height;
2334 ItemsCount = items_count;
2335 ItemsFrozen = 0;
2336 StepNo = 0;
2337 DisplayStart = -1;
2338 DisplayEnd = 0;
2339 }
2340
End()2341 void ImGuiListClipper::End()
2342 {
2343 if (ItemsCount < 0) // Already ended
2344 return;
2345
2346 // 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.
2347 if (ItemsCount < INT_MAX && DisplayStart >= 0)
2348 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2349 ItemsCount = -1;
2350 StepNo = 3;
2351 }
2352
Step()2353 bool ImGuiListClipper::Step()
2354 {
2355 ImGuiContext& g = *GImGui;
2356 ImGuiWindow* window = g.CurrentWindow;
2357
2358 ImGuiTable* table = g.CurrentTable;
2359 if (table && table->IsInsideRow)
2360 ImGui::TableEndRow(table);
2361
2362 // No items
2363 if (ItemsCount == 0 || GetSkipItemForListClipping())
2364 {
2365 End();
2366 return false;
2367 }
2368
2369 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2370 if (StepNo == 0)
2371 {
2372 // While we are in frozen row state, keep displaying items one by one, unclipped
2373 // FIXME: Could be stored as a table-agnostic state.
2374 if (table != NULL && !table->IsUnfrozenRows)
2375 {
2376 DisplayStart = ItemsFrozen;
2377 DisplayEnd = ItemsFrozen + 1;
2378 ItemsFrozen++;
2379 return true;
2380 }
2381
2382 StartPosY = window->DC.CursorPos.y;
2383 if (ItemsHeight <= 0.0f)
2384 {
2385 // Submit the first item so we can measure its height (generally it is 0..1)
2386 DisplayStart = ItemsFrozen;
2387 DisplayEnd = ItemsFrozen + 1;
2388 StepNo = 1;
2389 return true;
2390 }
2391
2392 // Already has item height (given by user in Begin): skip to calculating step
2393 DisplayStart = DisplayEnd;
2394 StepNo = 2;
2395 }
2396
2397 // Step 1: the clipper infer height from first element
2398 if (StepNo == 1)
2399 {
2400 IM_ASSERT(ItemsHeight <= 0.0f);
2401 if (table)
2402 {
2403 const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row
2404 const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell.
2405 ItemsHeight = pos_y2 - pos_y1;
2406 window->DC.CursorPos.y = pos_y2;
2407 }
2408 else
2409 {
2410 ItemsHeight = window->DC.CursorPos.y - StartPosY;
2411 }
2412 IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2413 StepNo = 2;
2414 }
2415
2416 // Reached end of list
2417 if (DisplayEnd >= ItemsCount)
2418 {
2419 End();
2420 return false;
2421 }
2422
2423 // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2424 if (StepNo == 2)
2425 {
2426 IM_ASSERT(ItemsHeight > 0.0f);
2427
2428 int already_submitted = DisplayEnd;
2429 ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2430 DisplayStart += already_submitted;
2431 DisplayEnd += already_submitted;
2432
2433 // Seek cursor
2434 if (DisplayStart > already_submitted)
2435 SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2436
2437 StepNo = 3;
2438 return true;
2439 }
2440
2441 // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2442 // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2443 if (StepNo == 3)
2444 {
2445 // Seek cursor
2446 if (ItemsCount < INT_MAX)
2447 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2448 ItemsCount = -1;
2449 return false;
2450 }
2451
2452 IM_ASSERT(0);
2453 return false;
2454 }
2455
2456 //-----------------------------------------------------------------------------
2457 // [SECTION] STYLING
2458 //-----------------------------------------------------------------------------
2459
GetStyle()2460 ImGuiStyle& ImGui::GetStyle()
2461 {
2462 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2463 return GImGui->Style;
2464 }
2465
GetColorU32(ImGuiCol idx,float alpha_mul)2466 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2467 {
2468 ImGuiStyle& style = GImGui->Style;
2469 ImVec4 c = style.Colors[idx];
2470 c.w *= style.Alpha * alpha_mul;
2471 return ColorConvertFloat4ToU32(c);
2472 }
2473
GetColorU32(const ImVec4 & col)2474 ImU32 ImGui::GetColorU32(const ImVec4& col)
2475 {
2476 ImGuiStyle& style = GImGui->Style;
2477 ImVec4 c = col;
2478 c.w *= style.Alpha;
2479 return ColorConvertFloat4ToU32(c);
2480 }
2481
GetStyleColorVec4(ImGuiCol idx)2482 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2483 {
2484 ImGuiStyle& style = GImGui->Style;
2485 return style.Colors[idx];
2486 }
2487
GetColorU32(ImU32 col)2488 ImU32 ImGui::GetColorU32(ImU32 col)
2489 {
2490 ImGuiStyle& style = GImGui->Style;
2491 if (style.Alpha >= 1.0f)
2492 return col;
2493 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2494 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2495 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2496 }
2497
2498 // 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
PushStyleColor(ImGuiCol idx,ImU32 col)2499 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2500 {
2501 ImGuiContext& g = *GImGui;
2502 ImGuiColorMod backup;
2503 backup.Col = idx;
2504 backup.BackupValue = g.Style.Colors[idx];
2505 g.ColorStack.push_back(backup);
2506 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2507 }
2508
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2509 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2510 {
2511 ImGuiContext& g = *GImGui;
2512 ImGuiColorMod backup;
2513 backup.Col = idx;
2514 backup.BackupValue = g.Style.Colors[idx];
2515 g.ColorStack.push_back(backup);
2516 g.Style.Colors[idx] = col;
2517 }
2518
PopStyleColor(int count)2519 void ImGui::PopStyleColor(int count)
2520 {
2521 ImGuiContext& g = *GImGui;
2522 while (count > 0)
2523 {
2524 ImGuiColorMod& backup = g.ColorStack.back();
2525 g.Style.Colors[backup.Col] = backup.BackupValue;
2526 g.ColorStack.pop_back();
2527 count--;
2528 }
2529 }
2530
2531 struct ImGuiStyleVarInfo
2532 {
2533 ImGuiDataType Type;
2534 ImU32 Count;
2535 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo2536 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2537 };
2538
2539 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2540 {
2541 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
2542 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha
2543 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
2544 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
2545 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
2546 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
2547 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
2548 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
2549 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
2550 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
2551 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
2552 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
2553 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
2554 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
2555 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
2556 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
2557 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
2558 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
2559 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
2560 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
2561 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
2562 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
2563 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
2564 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
2565 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2566 };
2567
GetStyleVarInfo(ImGuiStyleVar idx)2568 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2569 {
2570 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2571 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2572 return &GStyleVarInfo[idx];
2573 }
2574
PushStyleVar(ImGuiStyleVar idx,float val)2575 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2576 {
2577 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2578 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2579 {
2580 ImGuiContext& g = *GImGui;
2581 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2582 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2583 *pvar = val;
2584 return;
2585 }
2586 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2587 }
2588
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2589 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2590 {
2591 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2592 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2593 {
2594 ImGuiContext& g = *GImGui;
2595 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2596 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2597 *pvar = val;
2598 return;
2599 }
2600 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2601 }
2602
PopStyleVar(int count)2603 void ImGui::PopStyleVar(int count)
2604 {
2605 ImGuiContext& g = *GImGui;
2606 while (count > 0)
2607 {
2608 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2609 ImGuiStyleMod& backup = g.StyleVarStack.back();
2610 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2611 void* data = info->GetVarPtr(&g.Style);
2612 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
2613 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2614 g.StyleVarStack.pop_back();
2615 count--;
2616 }
2617 }
2618
GetStyleColorName(ImGuiCol idx)2619 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2620 {
2621 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2622 switch (idx)
2623 {
2624 case ImGuiCol_Text: return "Text";
2625 case ImGuiCol_TextDisabled: return "TextDisabled";
2626 case ImGuiCol_WindowBg: return "WindowBg";
2627 case ImGuiCol_ChildBg: return "ChildBg";
2628 case ImGuiCol_PopupBg: return "PopupBg";
2629 case ImGuiCol_Border: return "Border";
2630 case ImGuiCol_BorderShadow: return "BorderShadow";
2631 case ImGuiCol_FrameBg: return "FrameBg";
2632 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2633 case ImGuiCol_FrameBgActive: return "FrameBgActive";
2634 case ImGuiCol_TitleBg: return "TitleBg";
2635 case ImGuiCol_TitleBgActive: return "TitleBgActive";
2636 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2637 case ImGuiCol_MenuBarBg: return "MenuBarBg";
2638 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2639 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2640 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2641 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2642 case ImGuiCol_CheckMark: return "CheckMark";
2643 case ImGuiCol_SliderGrab: return "SliderGrab";
2644 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2645 case ImGuiCol_Button: return "Button";
2646 case ImGuiCol_ButtonHovered: return "ButtonHovered";
2647 case ImGuiCol_ButtonActive: return "ButtonActive";
2648 case ImGuiCol_Header: return "Header";
2649 case ImGuiCol_HeaderHovered: return "HeaderHovered";
2650 case ImGuiCol_HeaderActive: return "HeaderActive";
2651 case ImGuiCol_Separator: return "Separator";
2652 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2653 case ImGuiCol_SeparatorActive: return "SeparatorActive";
2654 case ImGuiCol_ResizeGrip: return "ResizeGrip";
2655 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2656 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2657 case ImGuiCol_Tab: return "Tab";
2658 case ImGuiCol_TabHovered: return "TabHovered";
2659 case ImGuiCol_TabActive: return "TabActive";
2660 case ImGuiCol_TabUnfocused: return "TabUnfocused";
2661 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2662 case ImGuiCol_PlotLines: return "PlotLines";
2663 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2664 case ImGuiCol_PlotHistogram: return "PlotHistogram";
2665 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2666 case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2667 case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2668 case ImGuiCol_TableBorderLight: return "TableBorderLight";
2669 case ImGuiCol_TableRowBg: return "TableRowBg";
2670 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2671 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2672 case ImGuiCol_DragDropTarget: return "DragDropTarget";
2673 case ImGuiCol_NavHighlight: return "NavHighlight";
2674 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2675 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2676 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2677 }
2678 IM_ASSERT(0);
2679 return "Unknown";
2680 }
2681
2682
2683 //-----------------------------------------------------------------------------
2684 // [SECTION] RENDER HELPERS
2685 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2686 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2687 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2688 //-----------------------------------------------------------------------------
2689
FindRenderedTextEnd(const char * text,const char * text_end)2690 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2691 {
2692 const char* text_display_end = text;
2693 if (!text_end)
2694 text_end = (const char*)-1;
2695
2696 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2697 text_display_end++;
2698 return text_display_end;
2699 }
2700
2701 // Internal ImGui functions to render text
2702 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2703 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2704 {
2705 ImGuiContext& g = *GImGui;
2706 ImGuiWindow* window = g.CurrentWindow;
2707
2708 // Hide anything after a '##' string
2709 const char* text_display_end;
2710 if (hide_text_after_hash)
2711 {
2712 text_display_end = FindRenderedTextEnd(text, text_end);
2713 }
2714 else
2715 {
2716 if (!text_end)
2717 text_end = text + strlen(text); // FIXME-OPT
2718 text_display_end = text_end;
2719 }
2720
2721 if (text != text_display_end)
2722 {
2723 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2724 if (g.LogEnabled)
2725 LogRenderedText(&pos, text, text_display_end);
2726 }
2727 }
2728
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2729 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2730 {
2731 ImGuiContext& g = *GImGui;
2732 ImGuiWindow* window = g.CurrentWindow;
2733
2734 if (!text_end)
2735 text_end = text + strlen(text); // FIXME-OPT
2736
2737 if (text != text_end)
2738 {
2739 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2740 if (g.LogEnabled)
2741 LogRenderedText(&pos, text, text_end);
2742 }
2743 }
2744
2745 // Default clip_rect uses (pos_min,pos_max)
2746 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
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)2747 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)
2748 {
2749 // Perform CPU side clipping for single clipped element to avoid using scissor state
2750 ImVec2 pos = pos_min;
2751 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2752
2753 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2754 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2755 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2756 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2757 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2758
2759 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2760 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2761 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2762
2763 // Render
2764 if (need_clipping)
2765 {
2766 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2767 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2768 }
2769 else
2770 {
2771 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2772 }
2773 }
2774
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)2775 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)
2776 {
2777 // Hide anything after a '##' string
2778 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2779 const int text_len = (int)(text_display_end - text);
2780 if (text_len == 0)
2781 return;
2782
2783 ImGuiContext& g = *GImGui;
2784 ImGuiWindow* window = g.CurrentWindow;
2785 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2786 if (g.LogEnabled)
2787 LogRenderedText(&pos_min, text, text_display_end);
2788 }
2789
2790
2791 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2792 // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.
2793 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
RenderTextEllipsis(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,float clip_max_x,float ellipsis_max_x,const char * text,const char * text_end_full,const ImVec2 * text_size_if_known)2794 void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
2795 {
2796 ImGuiContext& g = *GImGui;
2797 if (text_end_full == NULL)
2798 text_end_full = FindRenderedTextEnd(text);
2799 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2800
2801 //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));
2802 //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));
2803 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2804 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2805 if (text_size.x > pos_max.x - pos_min.x)
2806 {
2807 // Hello wo...
2808 // | | |
2809 // min max ellipsis_max
2810 // <-> this is generally some padding value
2811
2812 const ImFont* font = draw_list->_Data->Font;
2813 const float font_size = draw_list->_Data->FontSize;
2814 const char* text_end_ellipsis = NULL;
2815
2816 ImWchar ellipsis_char = font->EllipsisChar;
2817 int ellipsis_char_count = 1;
2818 if (ellipsis_char == (ImWchar)-1)
2819 {
2820 ellipsis_char = font->DotChar;
2821 ellipsis_char_count = 3;
2822 }
2823 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2824
2825 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2826 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2827
2828 if (ellipsis_char_count > 1)
2829 {
2830 // Full ellipsis size without free spacing after it.
2831 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2832 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2833 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2834 }
2835
2836 // We can now claim the space between pos_max.x and ellipsis_max.x
2837 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2838 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2839 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2840 {
2841 // Always display at least 1 character if there's no room for character + ellipsis
2842 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2843 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2844 }
2845 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2846 {
2847 // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
2848 text_end_ellipsis--;
2849 text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
2850 }
2851
2852 // Render text, render ellipsis
2853 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2854 float ellipsis_x = pos_min.x + text_size_clipped_x;
2855 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2856 for (int i = 0; i < ellipsis_char_count; i++)
2857 {
2858 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2859 ellipsis_x += ellipsis_glyph_width;
2860 }
2861 }
2862 else
2863 {
2864 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2865 }
2866
2867 if (g.LogEnabled)
2868 LogRenderedText(&pos_min, text, text_end_full);
2869 }
2870
2871 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2872 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2873 {
2874 ImGuiContext& g = *GImGui;
2875 ImGuiWindow* window = g.CurrentWindow;
2876 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2877 const float border_size = g.Style.FrameBorderSize;
2878 if (border && border_size > 0.0f)
2879 {
2880 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2881 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2882 }
2883 }
2884
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2885 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2886 {
2887 ImGuiContext& g = *GImGui;
2888 ImGuiWindow* window = g.CurrentWindow;
2889 const float border_size = g.Style.FrameBorderSize;
2890 if (border_size > 0.0f)
2891 {
2892 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2893 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2894 }
2895 }
2896
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2897 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2898 {
2899 ImGuiContext& g = *GImGui;
2900 if (id != g.NavId)
2901 return;
2902 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2903 return;
2904 ImGuiWindow* window = g.CurrentWindow;
2905 if (window->DC.NavHideHighlightOneFrame)
2906 return;
2907
2908 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2909 ImRect display_rect = bb;
2910 display_rect.ClipWith(window->ClipRect);
2911 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2912 {
2913 const float THICKNESS = 2.0f;
2914 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2915 display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2916 bool fully_visible = window->ClipRect.Contains(display_rect);
2917 if (!fully_visible)
2918 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2919 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, 0, THICKNESS);
2920 if (!fully_visible)
2921 window->DrawList->PopClipRect();
2922 }
2923 if (flags & ImGuiNavHighlightFlags_TypeThin)
2924 {
2925 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f);
2926 }
2927 }
2928
2929 //-----------------------------------------------------------------------------
2930 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2931 //-----------------------------------------------------------------------------
2932
2933 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2934 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2935 {
2936 memset(this, 0, sizeof(*this));
2937 Name = ImStrdup(name);
2938 NameBufLen = (int)strlen(name) + 1;
2939 ID = ImHashStr(name);
2940 IDStack.push_back(ID);
2941 MoveId = GetID("#MOVE");
2942 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2943 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2944 AutoFitFramesX = AutoFitFramesY = -1;
2945 AutoPosLastDirection = ImGuiDir_None;
2946 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2947 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2948 LastFrameActive = -1;
2949 LastTimeActive = -1.0f;
2950 FontWindowScale = 1.0f;
2951 SettingsOffset = -1;
2952 DrawList = &DrawListInst;
2953 DrawList->_Data = &context->DrawListSharedData;
2954 DrawList->_OwnerName = Name;
2955 }
2956
~ImGuiWindow()2957 ImGuiWindow::~ImGuiWindow()
2958 {
2959 IM_ASSERT(DrawList == &DrawListInst);
2960 IM_DELETE(Name);
2961 ColumnsStorage.clear_destruct();
2962 }
2963
GetID(const char * str,const char * str_end)2964 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2965 {
2966 ImGuiID seed = IDStack.back();
2967 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2968 ImGui::KeepAliveID(id);
2969 #ifdef IMGUI_ENABLE_TEST_ENGINE
2970 ImGuiContext& g = *GImGui;
2971 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2972 #endif
2973 return id;
2974 }
2975
GetID(const void * ptr)2976 ImGuiID ImGuiWindow::GetID(const void* ptr)
2977 {
2978 ImGuiID seed = IDStack.back();
2979 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2980 ImGui::KeepAliveID(id);
2981 #ifdef IMGUI_ENABLE_TEST_ENGINE
2982 ImGuiContext& g = *GImGui;
2983 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2984 #endif
2985 return id;
2986 }
2987
GetID(int n)2988 ImGuiID ImGuiWindow::GetID(int n)
2989 {
2990 ImGuiID seed = IDStack.back();
2991 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2992 ImGui::KeepAliveID(id);
2993 #ifdef IMGUI_ENABLE_TEST_ENGINE
2994 ImGuiContext& g = *GImGui;
2995 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2996 #endif
2997 return id;
2998 }
2999
GetIDNoKeepAlive(const char * str,const char * str_end)3000 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
3001 {
3002 ImGuiID seed = IDStack.back();
3003 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
3004 #ifdef IMGUI_ENABLE_TEST_ENGINE
3005 ImGuiContext& g = *GImGui;
3006 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
3007 #endif
3008 return id;
3009 }
3010
GetIDNoKeepAlive(const void * ptr)3011 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
3012 {
3013 ImGuiID seed = IDStack.back();
3014 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
3015 #ifdef IMGUI_ENABLE_TEST_ENGINE
3016 ImGuiContext& g = *GImGui;
3017 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
3018 #endif
3019 return id;
3020 }
3021
GetIDNoKeepAlive(int n)3022 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
3023 {
3024 ImGuiID seed = IDStack.back();
3025 ImGuiID id = ImHashData(&n, sizeof(n), seed);
3026 #ifdef IMGUI_ENABLE_TEST_ENGINE
3027 ImGuiContext& g = *GImGui;
3028 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
3029 #endif
3030 return id;
3031 }
3032
3033 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)3034 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
3035 {
3036 ImGuiID seed = IDStack.back();
3037 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) };
3038 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
3039 ImGui::KeepAliveID(id);
3040 return id;
3041 }
3042
SetCurrentWindow(ImGuiWindow * window)3043 static void SetCurrentWindow(ImGuiWindow* window)
3044 {
3045 ImGuiContext& g = *GImGui;
3046 g.CurrentWindow = window;
3047 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
3048 if (window)
3049 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
3050 }
3051
GcCompactTransientMiscBuffers()3052 void ImGui::GcCompactTransientMiscBuffers()
3053 {
3054 ImGuiContext& g = *GImGui;
3055 g.ItemFlagsStack.clear();
3056 g.GroupStack.clear();
3057 TableGcCompactSettings();
3058 }
3059
3060 // Free up/compact internal window buffers, we can use this when a window becomes unused.
3061 // Not freed:
3062 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
3063 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)3064 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
3065 {
3066 window->MemoryCompacted = true;
3067 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
3068 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
3069 window->IDStack.clear();
3070 window->DrawList->_ClearFreeMemory();
3071 window->DC.ChildWindows.clear();
3072 window->DC.ItemWidthStack.clear();
3073 window->DC.TextWrapPosStack.clear();
3074 }
3075
GcAwakeTransientWindowBuffers(ImGuiWindow * window)3076 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
3077 {
3078 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
3079 // The other buffers tends to amortize much faster.
3080 window->MemoryCompacted = false;
3081 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
3082 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
3083 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
3084 }
3085
SetActiveID(ImGuiID id,ImGuiWindow * window)3086 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3087 {
3088 ImGuiContext& g = *GImGui;
3089 g.ActiveIdIsJustActivated = (g.ActiveId != id);
3090 if (g.ActiveIdIsJustActivated)
3091 {
3092 g.ActiveIdTimer = 0.0f;
3093 g.ActiveIdHasBeenPressedBefore = false;
3094 g.ActiveIdHasBeenEditedBefore = false;
3095 g.ActiveIdMouseButton = -1;
3096 if (id != 0)
3097 {
3098 g.LastActiveId = id;
3099 g.LastActiveIdTimer = 0.0f;
3100 }
3101 }
3102 g.ActiveId = id;
3103 g.ActiveIdAllowOverlap = false;
3104 g.ActiveIdNoClearOnFocusLoss = false;
3105 g.ActiveIdWindow = window;
3106 g.ActiveIdHasBeenEditedThisFrame = false;
3107 if (id)
3108 {
3109 g.ActiveIdIsAlive = id;
3110 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3111 }
3112
3113 // Clear declaration of inputs claimed by the widget
3114 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3115 g.ActiveIdUsingMouseWheel = false;
3116 g.ActiveIdUsingNavDirMask = 0x00;
3117 g.ActiveIdUsingNavInputMask = 0x00;
3118 g.ActiveIdUsingKeyInputMask = 0x00;
3119 }
3120
ClearActiveID()3121 void ImGui::ClearActiveID()
3122 {
3123 SetActiveID(0, NULL); // g.ActiveId = 0;
3124 }
3125
SetHoveredID(ImGuiID id)3126 void ImGui::SetHoveredID(ImGuiID id)
3127 {
3128 ImGuiContext& g = *GImGui;
3129 g.HoveredId = id;
3130 g.HoveredIdAllowOverlap = false;
3131 g.HoveredIdUsingMouseWheel = false;
3132 if (id != 0 && g.HoveredIdPreviousFrame != id)
3133 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3134 }
3135
GetHoveredID()3136 ImGuiID ImGui::GetHoveredID()
3137 {
3138 ImGuiContext& g = *GImGui;
3139 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3140 }
3141
KeepAliveID(ImGuiID id)3142 void ImGui::KeepAliveID(ImGuiID id)
3143 {
3144 ImGuiContext& g = *GImGui;
3145 if (g.ActiveId == id)
3146 g.ActiveIdIsAlive = id;
3147 if (g.ActiveIdPreviousFrame == id)
3148 g.ActiveIdPreviousFrameIsAlive = true;
3149 }
3150
MarkItemEdited(ImGuiID id)3151 void ImGui::MarkItemEdited(ImGuiID id)
3152 {
3153 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3154 // 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.
3155 ImGuiContext& g = *GImGui;
3156 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3157 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3158 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3159 g.ActiveIdHasBeenEditedThisFrame = true;
3160 g.ActiveIdHasBeenEditedBefore = true;
3161 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
3162 }
3163
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3164 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3165 {
3166 // An active popup disable hovering on other windows (apart from its own children)
3167 // FIXME-OPT: This could be cached/stored within the window.
3168 ImGuiContext& g = *GImGui;
3169 if (g.NavWindow)
3170 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3171 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3172 {
3173 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3174 // NB: The order of those two tests is important because Modal windows are also Popups.
3175 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3176 return false;
3177 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3178 return false;
3179 }
3180 return true;
3181 }
3182
3183 // This is roughly matching the behavior of internal-facing ItemHoverable()
3184 // - 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()
3185 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3186 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3187 {
3188 ImGuiContext& g = *GImGui;
3189 ImGuiWindow* window = g.CurrentWindow;
3190 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3191 {
3192 if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3193 return false;
3194 return IsItemFocused();
3195 }
3196
3197 // Test for bounding box overlap, as updated as ItemAdd()
3198 ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
3199 if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
3200 return false;
3201 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
3202
3203 // Test if we are hovering the right window (our window could be behind another window)
3204 // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
3205 // [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
3206 // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
3207 // the test that has been running for a long while.
3208 if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
3209 if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0)
3210 return false;
3211
3212 // Test if another item is active (e.g. being dragged)
3213 if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
3214 if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3215 return false;
3216
3217 // Test if interactions on this window are blocked by an active popup or modal.
3218 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3219 if (!IsWindowContentHoverable(window, flags))
3220 return false;
3221
3222 // Test if the item is disabled
3223 if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3224 return false;
3225
3226 // Special handling for calling after Begin() which represent the title bar or tab.
3227 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3228 if (g.LastItemData.ID == window->MoveId && window->WriteAccessed)
3229 return false;
3230 return true;
3231 }
3232
3233 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3234 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3235 {
3236 ImGuiContext& g = *GImGui;
3237 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3238 return false;
3239
3240 ImGuiWindow* window = g.CurrentWindow;
3241 if (g.HoveredWindow != window)
3242 return false;
3243 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3244 return false;
3245 if (!IsMouseHoveringRect(bb.Min, bb.Max))
3246 return false;
3247 if (g.NavDisableMouseHover)
3248 return false;
3249 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
3250 {
3251 g.HoveredIdDisabled = true;
3252 return false;
3253 }
3254
3255 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3256 // hover test in widgets code. We could also decide to split this function is two.
3257 if (id != 0)
3258 SetHoveredID(id);
3259
3260 // When disabled we'll return false but still set HoveredId
3261 ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
3262 if (item_flags & ImGuiItemFlags_Disabled)
3263 {
3264 // Release active id if turning disabled
3265 if (g.ActiveId == id)
3266 ClearActiveID();
3267 g.HoveredIdDisabled = true;
3268 return false;
3269 }
3270
3271 if (id != 0)
3272 {
3273 // [DEBUG] Item Picker tool!
3274 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3275 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3276 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3277 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3278 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3279 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3280 if (g.DebugItemPickerBreakId == id)
3281 IM_DEBUG_BREAK();
3282 }
3283
3284 return true;
3285 }
3286
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3287 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3288 {
3289 ImGuiContext& g = *GImGui;
3290 ImGuiWindow* window = g.CurrentWindow;
3291 if (!bb.Overlaps(window->ClipRect))
3292 if (id == 0 || (id != g.ActiveId && id != g.NavId))
3293 if (clip_even_when_logged || !g.LogEnabled)
3294 return true;
3295 return false;
3296 }
3297
3298 // Called by ItemAdd()
3299 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
ItemFocusable(ImGuiWindow * window,ImGuiID id)3300 void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
3301 {
3302 ImGuiContext& g = *GImGui;
3303 IM_ASSERT(id != 0 && id == g.LastItemData.ID);
3304
3305 // Increment counters
3306 // FIXME: ImGuiItemFlags_Disabled should disable more.
3307 const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3308 window->DC.FocusCounterRegular++;
3309 if (is_tab_stop)
3310 {
3311 window->DC.FocusCounterTabStop++;
3312 if (g.NavId == id)
3313 g.NavIdTabCounter = window->DC.FocusCounterTabStop;
3314 }
3315
3316 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3317 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3318 if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL)
3319 {
3320 g.TabFocusRequestNextWindow = window;
3321 g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (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.
3322 }
3323
3324 // Handle focus requests
3325 if (g.TabFocusRequestCurrWindow == window)
3326 {
3327 if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular)
3328 {
3329 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode;
3330 return;
3331 }
3332 if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
3333 {
3334 g.NavJustTabbedId = id;
3335 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing;
3336 return;
3337 }
3338
3339 // If another item is about to be focused, we clear our own active id
3340 if (g.ActiveId == id)
3341 ClearActiveID();
3342 }
3343 }
3344
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3345 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3346 {
3347 if (wrap_pos_x < 0.0f)
3348 return 0.0f;
3349
3350 ImGuiContext& g = *GImGui;
3351 ImGuiWindow* window = g.CurrentWindow;
3352 if (wrap_pos_x == 0.0f)
3353 {
3354 // We could decide to setup a default wrapping max point for auto-resizing windows,
3355 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3356 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3357 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3358 //else
3359 wrap_pos_x = window->WorkRect.Max.x;
3360 }
3361 else if (wrap_pos_x > 0.0f)
3362 {
3363 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3364 }
3365
3366 return ImMax(wrap_pos_x - pos.x, 1.0f);
3367 }
3368
3369 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3370 void* ImGui::MemAlloc(size_t size)
3371 {
3372 if (ImGuiContext* ctx = GImGui)
3373 ctx->IO.MetricsActiveAllocations++;
3374 return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
3375 }
3376
3377 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3378 void ImGui::MemFree(void* ptr)
3379 {
3380 if (ptr)
3381 if (ImGuiContext* ctx = GImGui)
3382 ctx->IO.MetricsActiveAllocations--;
3383 return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
3384 }
3385
GetClipboardText()3386 const char* ImGui::GetClipboardText()
3387 {
3388 ImGuiContext& g = *GImGui;
3389 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3390 }
3391
SetClipboardText(const char * text)3392 void ImGui::SetClipboardText(const char* text)
3393 {
3394 ImGuiContext& g = *GImGui;
3395 if (g.IO.SetClipboardTextFn)
3396 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3397 }
3398
GetVersion()3399 const char* ImGui::GetVersion()
3400 {
3401 return IMGUI_VERSION;
3402 }
3403
3404 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3405 // 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
GetCurrentContext()3406 ImGuiContext* ImGui::GetCurrentContext()
3407 {
3408 return GImGui;
3409 }
3410
SetCurrentContext(ImGuiContext * ctx)3411 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3412 {
3413 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3414 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3415 #else
3416 GImGui = ctx;
3417 #endif
3418 }
3419
SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func,ImGuiMemFreeFunc free_func,void * user_data)3420 void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3421 {
3422 GImAllocatorAllocFunc = alloc_func;
3423 GImAllocatorFreeFunc = free_func;
3424 GImAllocatorUserData = user_data;
3425 }
3426
3427 // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
GetAllocatorFunctions(ImGuiMemAllocFunc * p_alloc_func,ImGuiMemFreeFunc * p_free_func,void ** p_user_data)3428 void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3429 {
3430 *p_alloc_func = GImAllocatorAllocFunc;
3431 *p_free_func = GImAllocatorFreeFunc;
3432 *p_user_data = GImAllocatorUserData;
3433 }
3434
CreateContext(ImFontAtlas * shared_font_atlas)3435 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3436 {
3437 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3438 if (GImGui == NULL)
3439 SetCurrentContext(ctx);
3440 Initialize(ctx);
3441 return ctx;
3442 }
3443
DestroyContext(ImGuiContext * ctx)3444 void ImGui::DestroyContext(ImGuiContext* ctx)
3445 {
3446 if (ctx == NULL)
3447 ctx = GImGui;
3448 Shutdown(ctx);
3449 if (GImGui == ctx)
3450 SetCurrentContext(NULL);
3451 IM_DELETE(ctx);
3452 }
3453
3454 // No specific ordering/dependency support, will see as needed
AddContextHook(ImGuiContext * ctx,const ImGuiContextHook * hook)3455 ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3456 {
3457 ImGuiContext& g = *ctx;
3458 IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
3459 g.Hooks.push_back(*hook);
3460 g.Hooks.back().HookId = ++g.HookIdNext;
3461 return g.HookIdNext;
3462 }
3463
3464 // Deferred removal, avoiding issue with changing vector while iterating it
RemoveContextHook(ImGuiContext * ctx,ImGuiID hook_id)3465 void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
3466 {
3467 ImGuiContext& g = *ctx;
3468 IM_ASSERT(hook_id != 0);
3469 for (int n = 0; n < g.Hooks.Size; n++)
3470 if (g.Hooks[n].HookId == hook_id)
3471 g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_;
3472 }
3473
3474 // Call context hooks (used by e.g. test engine)
3475 // We assume a small number of hooks so all stored in same array
CallContextHooks(ImGuiContext * ctx,ImGuiContextHookType hook_type)3476 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3477 {
3478 ImGuiContext& g = *ctx;
3479 for (int n = 0; n < g.Hooks.Size; n++)
3480 if (g.Hooks[n].Type == hook_type)
3481 g.Hooks[n].Callback(&g, &g.Hooks[n]);
3482 }
3483
GetIO()3484 ImGuiIO& ImGui::GetIO()
3485 {
3486 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3487 return GImGui->IO;
3488 }
3489
3490 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
GetDrawData()3491 ImDrawData* ImGui::GetDrawData()
3492 {
3493 ImGuiContext& g = *GImGui;
3494 ImGuiViewportP* viewport = g.Viewports[0];
3495 return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
3496 }
3497
GetTime()3498 double ImGui::GetTime()
3499 {
3500 return GImGui->Time;
3501 }
3502
GetFrameCount()3503 int ImGui::GetFrameCount()
3504 {
3505 return GImGui->FrameCount;
3506 }
3507
GetViewportDrawList(ImGuiViewportP * viewport,size_t drawlist_no,const char * drawlist_name)3508 static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
3509 {
3510 // Create the draw list on demand, because they are not frequently used for all viewports
3511 ImGuiContext& g = *GImGui;
3512 IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
3513 ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
3514 if (draw_list == NULL)
3515 {
3516 draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
3517 draw_list->_OwnerName = drawlist_name;
3518 viewport->DrawLists[drawlist_no] = draw_list;
3519 }
3520
3521 // Our ImDrawList system requires that there is always a command
3522 if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount)
3523 {
3524 draw_list->_ResetForNewFrame();
3525 draw_list->PushTextureID(g.IO.Fonts->TexID);
3526 draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
3527 viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount;
3528 }
3529 return draw_list;
3530 }
3531
GetBackgroundDrawList(ImGuiViewport * viewport)3532 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
3533 {
3534 return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
3535 }
3536
GetBackgroundDrawList()3537 ImDrawList* ImGui::GetBackgroundDrawList()
3538 {
3539 ImGuiContext& g = *GImGui;
3540 return GetBackgroundDrawList(g.Viewports[0]);
3541 }
3542
GetForegroundDrawList(ImGuiViewport * viewport)3543 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
3544 {
3545 return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
3546 }
3547
GetForegroundDrawList()3548 ImDrawList* ImGui::GetForegroundDrawList()
3549 {
3550 ImGuiContext& g = *GImGui;
3551 return GetForegroundDrawList(g.Viewports[0]);
3552 }
3553
GetDrawListSharedData()3554 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3555 {
3556 return &GImGui->DrawListSharedData;
3557 }
3558
StartMouseMovingWindow(ImGuiWindow * window)3559 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3560 {
3561 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3562 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3563 // This is because we want ActiveId to be set even when the window is not permitted to move.
3564 ImGuiContext& g = *GImGui;
3565 FocusWindow(window);
3566 SetActiveID(window->MoveId, window);
3567 g.NavDisableHighlight = true;
3568 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
3569 g.ActiveIdNoClearOnFocusLoss = true;
3570 SetActiveIdUsingNavAndKeys();
3571
3572 bool can_move_window = true;
3573 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3574 can_move_window = false;
3575 if (can_move_window)
3576 g.MovingWindow = window;
3577 }
3578
3579 // Handle mouse moving window
3580 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3581 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3582 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3583 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3584 void ImGui::UpdateMouseMovingWindowNewFrame()
3585 {
3586 ImGuiContext& g = *GImGui;
3587 if (g.MovingWindow != NULL)
3588 {
3589 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3590 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3591 KeepAliveID(g.ActiveId);
3592 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3593 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3594 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3595 {
3596 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3597 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3598 {
3599 MarkIniSettingsDirty(moving_window);
3600 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3601 }
3602 FocusWindow(g.MovingWindow);
3603 }
3604 else
3605 {
3606 g.MovingWindow = NULL;
3607 ClearActiveID();
3608 }
3609 }
3610 else
3611 {
3612 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3613 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3614 {
3615 KeepAliveID(g.ActiveId);
3616 if (!g.IO.MouseDown[0])
3617 ClearActiveID();
3618 }
3619 }
3620 }
3621
3622 // Initiate moving window when clicking on empty space or title bar.
3623 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3624 void ImGui::UpdateMouseMovingWindowEndFrame()
3625 {
3626 ImGuiContext& g = *GImGui;
3627 if (g.ActiveId != 0 || g.HoveredId != 0)
3628 return;
3629
3630 // Unless we just made a window/popup appear
3631 if (g.NavWindow && g.NavWindow->Appearing)
3632 return;
3633
3634 // Click on empty space to focus window and start moving
3635 // (after we're done with all our widgets)
3636 if (g.IO.MouseClicked[0])
3637 {
3638 // Handle the edge case of a popup being closed while clicking in its empty space.
3639 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3640 ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3641 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3642
3643 if (root_window != NULL && !is_closed_popup)
3644 {
3645 StartMouseMovingWindow(g.HoveredWindow); //-V595
3646
3647 // Cancel moving if clicked outside of title bar
3648 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
3649 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3650 g.MovingWindow = NULL;
3651
3652 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3653 if (g.HoveredIdDisabled)
3654 g.MovingWindow = NULL;
3655 }
3656 else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3657 {
3658 // Clicking on void disable focus
3659 FocusWindow(NULL);
3660 }
3661 }
3662
3663 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3664 // Instead, focus will be restored to the window under the bottom-most closed popup.
3665 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3666 if (g.IO.MouseClicked[1])
3667 {
3668 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3669 // This is where we can trim the popup stack.
3670 ImGuiWindow* modal = GetTopMostPopupModal();
3671 bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3672 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3673 }
3674 }
3675
IsWindowActiveAndVisible(ImGuiWindow * window)3676 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3677 {
3678 return (window->Active) && (!window->Hidden);
3679 }
3680
UpdateMouseInputs()3681 static void ImGui::UpdateMouseInputs()
3682 {
3683 ImGuiContext& g = *GImGui;
3684
3685 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3686 if (IsMousePosValid(&g.IO.MousePos))
3687 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3688
3689 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3690 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3691 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3692 else
3693 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3694 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3695 g.NavDisableMouseHover = false;
3696
3697 g.IO.MousePosPrev = g.IO.MousePos;
3698 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3699 {
3700 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3701 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3702 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3703 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;
3704 g.IO.MouseDoubleClicked[i] = false;
3705 if (g.IO.MouseClicked[i])
3706 {
3707 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3708 {
3709 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3710 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3711 g.IO.MouseDoubleClicked[i] = true;
3712 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3713 }
3714 else
3715 {
3716 g.IO.MouseClickedTime[i] = g.Time;
3717 }
3718 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3719 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3720 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3721 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3722 }
3723 else if (g.IO.MouseDown[i])
3724 {
3725 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3726 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3727 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3728 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);
3729 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);
3730 }
3731 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3732 g.IO.MouseDownWasDoubleClick[i] = false;
3733 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3734 g.NavDisableMouseHover = false;
3735 }
3736 }
3737
StartLockWheelingWindow(ImGuiWindow * window)3738 static void StartLockWheelingWindow(ImGuiWindow* window)
3739 {
3740 ImGuiContext& g = *GImGui;
3741 if (g.WheelingWindow == window)
3742 return;
3743 g.WheelingWindow = window;
3744 g.WheelingWindowRefMousePos = g.IO.MousePos;
3745 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3746 }
3747
UpdateMouseWheel()3748 void ImGui::UpdateMouseWheel()
3749 {
3750 ImGuiContext& g = *GImGui;
3751
3752 // Reset the locked window if we move the mouse or after the timer elapses
3753 if (g.WheelingWindow != NULL)
3754 {
3755 g.WheelingWindowTimer -= g.IO.DeltaTime;
3756 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3757 g.WheelingWindowTimer = 0.0f;
3758 if (g.WheelingWindowTimer <= 0.0f)
3759 {
3760 g.WheelingWindow = NULL;
3761 g.WheelingWindowTimer = 0.0f;
3762 }
3763 }
3764
3765 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3766 return;
3767
3768 if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3769 return;
3770
3771 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3772 if (!window || window->Collapsed)
3773 return;
3774
3775 // Zoom / Scale window
3776 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3777 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3778 {
3779 StartLockWheelingWindow(window);
3780 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3781 const float scale = new_font_scale / window->FontWindowScale;
3782 window->FontWindowScale = new_font_scale;
3783 if (window == window->RootWindow)
3784 {
3785 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3786 SetWindowPos(window, window->Pos + offset, 0);
3787 window->Size = ImFloor(window->Size * scale);
3788 window->SizeFull = ImFloor(window->SizeFull * scale);
3789 }
3790 return;
3791 }
3792
3793 // Mouse wheel scrolling
3794 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3795 if (g.IO.KeyCtrl)
3796 return;
3797
3798 // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
3799 // (we avoid doing it on OSX as it the OS input layer handles this already)
3800 const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors;
3801 const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel;
3802 const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH;
3803
3804 // Vertical Mouse Wheel scrolling
3805 if (wheel_y != 0.0f)
3806 {
3807 StartLockWheelingWindow(window);
3808 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3809 window = window->ParentWindow;
3810 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3811 {
3812 float max_step = window->InnerRect.GetHeight() * 0.67f;
3813 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3814 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3815 }
3816 }
3817
3818 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3819 if (wheel_x != 0.0f)
3820 {
3821 StartLockWheelingWindow(window);
3822 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3823 window = window->ParentWindow;
3824 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3825 {
3826 float max_step = window->InnerRect.GetWidth() * 0.67f;
3827 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3828 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3829 }
3830 }
3831 }
3832
UpdateTabFocus()3833 void ImGui::UpdateTabFocus()
3834 {
3835 ImGuiContext& g = *GImGui;
3836
3837 // Pressing TAB activate widget focus
3838 g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3839 if (g.ActiveId == 0 && g.TabFocusPressed)
3840 {
3841 // - This path is only taken when no widget are active/tabbed-into yet.
3842 // Subsequent tabbing will be processed by FocusableItemRegister()
3843 // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3844 // manipulate the Next fields here even though they will be turned into Curr fields below.
3845 g.TabFocusRequestNextWindow = g.NavWindow;
3846 g.TabFocusRequestNextCounterRegular = INT_MAX;
3847 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3848 g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0);
3849 else
3850 g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3851 }
3852
3853 // Turn queued focus request into current one
3854 g.TabFocusRequestCurrWindow = NULL;
3855 g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX;
3856 if (g.TabFocusRequestNextWindow != NULL)
3857 {
3858 ImGuiWindow* window = g.TabFocusRequestNextWindow;
3859 g.TabFocusRequestCurrWindow = window;
3860 if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3861 g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3862 if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3863 g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3864 g.TabFocusRequestNextWindow = NULL;
3865 g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX;
3866 }
3867
3868 g.NavIdTabCounter = INT_MAX;
3869 }
3870
3871 // 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)
UpdateHoveredWindowAndCaptureFlags()3872 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3873 {
3874 ImGuiContext& g = *GImGui;
3875 ImGuiIO& io = g.IO;
3876 g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
3877
3878 // Find the window hovered by mouse:
3879 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3880 // - 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.
3881 // - 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.
3882 bool clear_hovered_windows = false;
3883 FindHoveredWindow();
3884
3885 // Modal windows prevents mouse from hovering behind them.
3886 ImGuiWindow* modal_window = GetTopMostPopupModal();
3887 if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window))
3888 clear_hovered_windows = true;
3889
3890 // Disabled mouse?
3891 if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
3892 clear_hovered_windows = true;
3893
3894 // We track click ownership. When clicked outside of a window the click is owned by the application and
3895 // won't report hovering nor request capture even while dragging over our windows afterward.
3896 const bool has_open_popup = (g.OpenPopupStack.Size > 0);
3897 const bool has_open_modal = (modal_window != NULL);
3898 int mouse_earliest_down = -1;
3899 bool mouse_any_down = false;
3900 for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
3901 {
3902 if (io.MouseClicked[i])
3903 {
3904 io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
3905 io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
3906 }
3907 mouse_any_down |= io.MouseDown[i];
3908 if (io.MouseDown[i])
3909 if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
3910 mouse_earliest_down = i;
3911 }
3912 const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
3913 const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
3914
3915 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3916 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3917 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3918 if (!mouse_avail && !mouse_dragging_extern_payload)
3919 clear_hovered_windows = true;
3920
3921 if (clear_hovered_windows)
3922 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
3923
3924 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
3925 // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
3926 if (g.WantCaptureMouseNextFrame != -1)
3927 {
3928 io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
3929 }
3930 else
3931 {
3932 io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
3933 io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
3934 }
3935
3936 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
3937 if (g.WantCaptureKeyboardNextFrame != -1)
3938 io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3939 else
3940 io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3941 if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3942 io.WantCaptureKeyboard = true;
3943
3944 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3945 io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3946 }
3947
GetMergedKeyModFlags()3948 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3949 {
3950 ImGuiContext& g = *GImGui;
3951 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3952 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3953 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3954 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3955 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; }
3956 return key_mod_flags;
3957 }
3958
NewFrame()3959 void ImGui::NewFrame()
3960 {
3961 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3962 ImGuiContext& g = *GImGui;
3963
3964 // Remove pending delete hooks before frame start.
3965 // This deferred removal avoid issues of removal while iterating the hook vector
3966 for (int n = g.Hooks.Size - 1; n >= 0; n--)
3967 if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
3968 g.Hooks.erase(&g.Hooks[n]);
3969
3970 CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
3971
3972 // Check and assert for various common IO and Configuration mistakes
3973 ErrorCheckNewFrameSanityChecks();
3974
3975 // Load settings on first frame, save settings when modified (after a delay)
3976 UpdateSettings();
3977
3978 g.Time += g.IO.DeltaTime;
3979 g.WithinFrameScope = true;
3980 g.FrameCount += 1;
3981 g.TooltipOverrideCount = 0;
3982 g.WindowsActiveCount = 0;
3983 g.MenusIdSubmittedThisFrame.resize(0);
3984
3985 // Calculate frame-rate for the user, as a purely luxurious feature
3986 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3987 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3988 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3989 g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
3990 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
3991
3992 UpdateViewportsNewFrame();
3993
3994 // Setup current font and draw list shared data
3995 g.IO.Fonts->Locked = true;
3996 SetCurrentFont(GetDefaultFont());
3997 IM_ASSERT(g.Font->IsLoaded());
3998 ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
3999 for (int n = 0; n < g.Viewports.Size; n++)
4000 virtual_space.Add(g.Viewports[n]->GetMainRect());
4001 g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
4002 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
4003 g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
4004 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
4005 if (g.Style.AntiAliasedLines)
4006 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
4007 if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
4008 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
4009 if (g.Style.AntiAliasedFill)
4010 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
4011 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
4012 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
4013
4014 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
4015 for (int n = 0; n < g.Viewports.Size; n++)
4016 {
4017 ImGuiViewportP* viewport = g.Viewports[n];
4018 viewport->DrawDataP.Clear();
4019 }
4020
4021 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
4022 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
4023 KeepAliveID(g.DragDropPayload.SourceId);
4024
4025 // Update HoveredId data
4026 if (!g.HoveredIdPreviousFrame)
4027 g.HoveredIdTimer = 0.0f;
4028 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
4029 g.HoveredIdNotActiveTimer = 0.0f;
4030 if (g.HoveredId)
4031 g.HoveredIdTimer += g.IO.DeltaTime;
4032 if (g.HoveredId && g.ActiveId != g.HoveredId)
4033 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
4034 g.HoveredIdPreviousFrame = g.HoveredId;
4035 g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
4036 g.HoveredId = 0;
4037 g.HoveredIdAllowOverlap = false;
4038 g.HoveredIdUsingMouseWheel = false;
4039 g.HoveredIdDisabled = false;
4040
4041 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
4042 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
4043 ClearActiveID();
4044 if (g.ActiveId)
4045 g.ActiveIdTimer += g.IO.DeltaTime;
4046 g.LastActiveIdTimer += g.IO.DeltaTime;
4047 g.ActiveIdPreviousFrame = g.ActiveId;
4048 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
4049 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4050 g.ActiveIdIsAlive = 0;
4051 g.ActiveIdHasBeenEditedThisFrame = false;
4052 g.ActiveIdPreviousFrameIsAlive = false;
4053 g.ActiveIdIsJustActivated = false;
4054 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
4055 g.TempInputId = 0;
4056 if (g.ActiveId == 0)
4057 {
4058 g.ActiveIdUsingNavDirMask = 0x00;
4059 g.ActiveIdUsingNavInputMask = 0x00;
4060 g.ActiveIdUsingKeyInputMask = 0x00;
4061 }
4062
4063 // Drag and drop
4064 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
4065 g.DragDropAcceptIdCurr = 0;
4066 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
4067 g.DragDropWithinSource = false;
4068 g.DragDropWithinTarget = false;
4069 g.DragDropHoldJustPressedId = 0;
4070
4071 // Update keyboard input state
4072 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
4073 g.IO.KeyMods = GetMergedKeyModFlags();
4074 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
4075 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
4076 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;
4077
4078 // Update gamepad/keyboard navigation
4079 NavUpdate();
4080
4081 // Update mouse input state
4082 UpdateMouseInputs();
4083
4084 // Find hovered window
4085 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
4086 UpdateHoveredWindowAndCaptureFlags();
4087
4088 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
4089 UpdateMouseMovingWindowNewFrame();
4090
4091 // Background darkening/whitening
4092 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
4093 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
4094 else
4095 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
4096
4097 g.MouseCursor = ImGuiMouseCursor_Arrow;
4098 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
4099 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
4100
4101 // Mouse wheel scrolling, scale
4102 UpdateMouseWheel();
4103
4104 // Update legacy TAB focus
4105 UpdateTabFocus();
4106
4107 // Mark all windows as not visible and compact unused memory.
4108 IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
4109 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
4110 for (int i = 0; i != g.Windows.Size; i++)
4111 {
4112 ImGuiWindow* window = g.Windows[i];
4113 window->WasActive = window->Active;
4114 window->BeginCount = 0;
4115 window->Active = false;
4116 window->WriteAccessed = false;
4117
4118 // Garbage collect transient buffers of recently unused windows
4119 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
4120 GcCompactTransientWindowBuffers(window);
4121 }
4122
4123 // Garbage collect transient buffers of recently unused tables
4124 for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
4125 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
4126 TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
4127 for (int i = 0; i < g.TablesTempDataStack.Size; i++)
4128 if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time)
4129 TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]);
4130 if (g.GcCompactAll)
4131 GcCompactTransientMiscBuffers();
4132 g.GcCompactAll = false;
4133
4134 // Closing the focused window restore focus to the first active root window in descending z-order
4135 if (g.NavWindow && !g.NavWindow->WasActive)
4136 FocusTopMostWindowUnderOne(NULL, NULL);
4137
4138 // No window should be open at the beginning of the frame.
4139 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
4140 g.CurrentWindowStack.resize(0);
4141 g.BeginPopupStack.resize(0);
4142 g.ItemFlagsStack.resize(0);
4143 g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
4144 g.GroupStack.resize(0);
4145
4146 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4147 UpdateDebugToolItemPicker();
4148
4149 // Create implicit/fallback window - which we will only render it if the user has added something to it.
4150 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
4151 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
4152 g.WithinFrameScopeWithImplicitWindow = true;
4153 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
4154 Begin("Debug##Default");
4155 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
4156
4157 CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
4158 }
4159
4160 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()4161 void ImGui::UpdateDebugToolItemPicker()
4162 {
4163 ImGuiContext& g = *GImGui;
4164 g.DebugItemPickerBreakId = 0;
4165 if (g.DebugItemPickerActive)
4166 {
4167 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
4168 SetMouseCursor(ImGuiMouseCursor_Hand);
4169 if (IsKeyPressedMap(ImGuiKey_Escape))
4170 g.DebugItemPickerActive = false;
4171 if (IsMouseClicked(0) && hovered_id)
4172 {
4173 g.DebugItemPickerBreakId = hovered_id;
4174 g.DebugItemPickerActive = false;
4175 }
4176 SetNextWindowBgAlpha(0.60f);
4177 BeginTooltip();
4178 Text("HoveredId: 0x%08X", hovered_id);
4179 Text("Press ESC to abort picking.");
4180 TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
4181 EndTooltip();
4182 }
4183 }
4184
Initialize(ImGuiContext * context)4185 void ImGui::Initialize(ImGuiContext* context)
4186 {
4187 ImGuiContext& g = *context;
4188 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4189
4190 // Add .ini handle for ImGuiWindow type
4191 {
4192 ImGuiSettingsHandler ini_handler;
4193 ini_handler.TypeName = "Window";
4194 ini_handler.TypeHash = ImHashStr("Window");
4195 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4196 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4197 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4198 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4199 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4200 g.SettingsHandlers.push_back(ini_handler);
4201 }
4202
4203 // Add .ini handle for ImGuiTable type
4204 TableSettingsInstallHandler(context);
4205
4206 // Create default viewport
4207 ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4208 g.Viewports.push_back(viewport);
4209
4210 #ifdef IMGUI_HAS_DOCK
4211 #endif
4212
4213 g.Initialized = true;
4214 }
4215
4216 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)4217 void ImGui::Shutdown(ImGuiContext* context)
4218 {
4219 // 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)
4220 ImGuiContext& g = *context;
4221 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4222 {
4223 g.IO.Fonts->Locked = false;
4224 IM_DELETE(g.IO.Fonts);
4225 }
4226 g.IO.Fonts = NULL;
4227
4228 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4229 if (!g.Initialized)
4230 return;
4231
4232 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4233 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4234 {
4235 ImGuiContext* backup_context = GImGui;
4236 SetCurrentContext(&g);
4237 SaveIniSettingsToDisk(g.IO.IniFilename);
4238 SetCurrentContext(backup_context);
4239 }
4240
4241 CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4242
4243 // Clear everything else
4244 g.Windows.clear_delete();
4245 g.WindowsFocusOrder.clear();
4246 g.WindowsTempSortBuffer.clear();
4247 g.CurrentWindow = NULL;
4248 g.CurrentWindowStack.clear();
4249 g.WindowsById.Clear();
4250 g.NavWindow = NULL;
4251 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4252 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4253 g.MovingWindow = NULL;
4254 g.ColorStack.clear();
4255 g.StyleVarStack.clear();
4256 g.FontStack.clear();
4257 g.OpenPopupStack.clear();
4258 g.BeginPopupStack.clear();
4259
4260 g.Viewports.clear_delete();
4261
4262 g.TabBars.Clear();
4263 g.CurrentTabBarStack.clear();
4264 g.ShrinkWidthBuffer.clear();
4265
4266 g.Tables.Clear();
4267 g.TablesTempDataStack.clear_destruct();
4268 g.DrawChannelsTempMergeBuffer.clear();
4269
4270 g.ClipboardHandlerData.clear();
4271 g.MenusIdSubmittedThisFrame.clear();
4272 g.InputTextState.ClearFreeMemory();
4273
4274 g.SettingsWindows.clear();
4275 g.SettingsHandlers.clear();
4276
4277 if (g.LogFile)
4278 {
4279 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4280 if (g.LogFile != stdout)
4281 #endif
4282 ImFileClose(g.LogFile);
4283 g.LogFile = NULL;
4284 }
4285 g.LogBuffer.clear();
4286
4287 g.Initialized = false;
4288 }
4289
4290 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4291 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4292 {
4293 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4294 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4295 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4296 return d;
4297 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4298 return d;
4299 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4300 }
4301
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4302 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4303 {
4304 out_sorted_windows->push_back(window);
4305 if (window->Active)
4306 {
4307 int count = window->DC.ChildWindows.Size;
4308 if (count > 1)
4309 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4310 for (int i = 0; i < count; i++)
4311 {
4312 ImGuiWindow* child = window->DC.ChildWindows[i];
4313 if (child->Active)
4314 AddWindowToSortBuffer(out_sorted_windows, child);
4315 }
4316 }
4317 }
4318
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4319 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4320 {
4321 // Remove trailing command if unused.
4322 // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4323 draw_list->_PopUnusedDrawCmd();
4324 if (draw_list->CmdBuffer.Size == 0)
4325 return;
4326
4327 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4328 // May trigger for you if you are using PrimXXX functions incorrectly.
4329 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4330 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4331 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4332 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4333
4334 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4335 // If this assert triggers because you are drawing lots of stuff manually:
4336 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4337 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4338 // - If you want large meshes with more than 64K vertices, you can either:
4339 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4340 // Most example backends already support this from 1.71. Pre-1.71 backends won't.
4341 // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
4342 // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4343 // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4344 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4345 // Your own engine or render API may use different parameters or function calls to specify index sizes.
4346 // 2 and 4 bytes indices are generally supported by most graphics API.
4347 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4348 // the 64K limit to split your draw commands in multiple draw lists.
4349 if (sizeof(ImDrawIdx) == 2)
4350 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4351
4352 out_list->push_back(draw_list);
4353 }
4354
AddWindowToDrawData(ImGuiWindow * window,int layer)4355 static void AddWindowToDrawData(ImGuiWindow* window, int layer)
4356 {
4357 ImGuiContext& g = *GImGui;
4358 ImGuiViewportP* viewport = g.Viewports[0];
4359 g.IO.MetricsRenderWindows++;
4360 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList);
4361 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4362 {
4363 ImGuiWindow* child = window->DC.ChildWindows[i];
4364 if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
4365 AddWindowToDrawData(child, layer);
4366 }
4367 }
4368
4369 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4370 static void AddRootWindowToDrawData(ImGuiWindow* window)
4371 {
4372 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4373 AddWindowToDrawData(window, layer);
4374 }
4375
FlattenIntoSingleLayer()4376 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4377 {
4378 int n = Layers[0].Size;
4379 int size = n;
4380 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4381 size += Layers[i].Size;
4382 Layers[0].resize(size);
4383 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4384 {
4385 ImVector<ImDrawList*>& layer = Layers[layer_n];
4386 if (layer.empty())
4387 continue;
4388 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4389 n += layer.Size;
4390 layer.resize(0);
4391 }
4392 }
4393
SetupViewportDrawData(ImGuiViewportP * viewport,ImVector<ImDrawList * > * draw_lists)4394 static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
4395 {
4396 ImGuiIO& io = ImGui::GetIO();
4397 ImDrawData* draw_data = &viewport->DrawDataP;
4398 draw_data->Valid = true;
4399 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4400 draw_data->CmdListsCount = draw_lists->Size;
4401 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4402 draw_data->DisplayPos = viewport->Pos;
4403 draw_data->DisplaySize = viewport->Size;
4404 draw_data->FramebufferScale = io.DisplayFramebufferScale;
4405 for (int n = 0; n < draw_lists->Size; n++)
4406 {
4407 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4408 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4409 }
4410 }
4411
4412 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4413 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4414 // so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4415 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4416 // some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4417 // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4418 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4419 {
4420 ImGuiWindow* window = GetCurrentWindow();
4421 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4422 window->ClipRect = window->DrawList->_ClipRectStack.back();
4423 }
4424
PopClipRect()4425 void ImGui::PopClipRect()
4426 {
4427 ImGuiWindow* window = GetCurrentWindow();
4428 window->DrawList->PopClipRect();
4429 window->ClipRect = window->DrawList->_ClipRectStack.back();
4430 }
4431
4432 // 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.
EndFrame()4433 void ImGui::EndFrame()
4434 {
4435 ImGuiContext& g = *GImGui;
4436 IM_ASSERT(g.Initialized);
4437
4438 // Don't process EndFrame() multiple times.
4439 if (g.FrameCountEnded == g.FrameCount)
4440 return;
4441 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4442
4443 CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4444
4445 ErrorCheckEndFrameSanityChecks();
4446
4447 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4448 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4449 {
4450 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4451 g.PlatformImeLastPos = g.PlatformImePos;
4452 }
4453
4454 // Hide implicit/fallback "Debug" window if it hasn't been used
4455 g.WithinFrameScopeWithImplicitWindow = false;
4456 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4457 g.CurrentWindow->Active = false;
4458 End();
4459
4460 // Update navigation: CTRL+Tab, wrap-around requests
4461 NavEndFrame();
4462
4463 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4464 if (g.DragDropActive)
4465 {
4466 bool is_delivered = g.DragDropPayload.Delivery;
4467 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4468 if (is_delivered || is_elapsed)
4469 ClearDragDrop();
4470 }
4471
4472 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4473 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4474 {
4475 g.DragDropWithinSource = true;
4476 SetTooltip("...");
4477 g.DragDropWithinSource = false;
4478 }
4479
4480 // End frame
4481 g.WithinFrameScope = false;
4482 g.FrameCountEnded = g.FrameCount;
4483
4484 // Initiate moving window + handle left-click and right-click focus
4485 UpdateMouseMovingWindowEndFrame();
4486
4487 // Sort the window list so that all child windows are after their parent
4488 // We cannot do that on FocusWindow() because children may not exist yet
4489 g.WindowsTempSortBuffer.resize(0);
4490 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4491 for (int i = 0; i != g.Windows.Size; i++)
4492 {
4493 ImGuiWindow* window = g.Windows[i];
4494 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4495 continue;
4496 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4497 }
4498
4499 // 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.
4500 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4501 g.Windows.swap(g.WindowsTempSortBuffer);
4502 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4503
4504 // Unlock font atlas
4505 g.IO.Fonts->Locked = false;
4506
4507 // Clear Input data for next frame
4508 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4509 g.IO.InputQueueCharacters.resize(0);
4510 g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it.
4511 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4512
4513 CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4514 }
4515
4516 // Prepare the data for rendering so you can call GetDrawData()
4517 // (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
4518 // it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
Render()4519 void ImGui::Render()
4520 {
4521 ImGuiContext& g = *GImGui;
4522 IM_ASSERT(g.Initialized);
4523
4524 if (g.FrameCountEnded != g.FrameCount)
4525 EndFrame();
4526 g.FrameCountRendered = g.FrameCount;
4527 g.IO.MetricsRenderWindows = 0;
4528
4529 CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4530
4531 // Add background ImDrawList (for each active viewport)
4532 for (int n = 0; n != g.Viewports.Size; n++)
4533 {
4534 ImGuiViewportP* viewport = g.Viewports[n];
4535 viewport->DrawDataBuilder.Clear();
4536 if (viewport->DrawLists[0] != NULL)
4537 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
4538 }
4539
4540 // Add ImDrawList to render
4541 ImGuiWindow* windows_to_render_top_most[2];
4542 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4543 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4544 for (int n = 0; n != g.Windows.Size; n++)
4545 {
4546 ImGuiWindow* window = g.Windows[n];
4547 IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
4548 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4549 AddRootWindowToDrawData(window);
4550 }
4551 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4552 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4553 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4554
4555 // Setup ImDrawData structures for end-user
4556 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
4557 for (int n = 0; n < g.Viewports.Size; n++)
4558 {
4559 ImGuiViewportP* viewport = g.Viewports[n];
4560 viewport->DrawDataBuilder.FlattenIntoSingleLayer();
4561
4562 // Draw software mouse cursor if requested by io.MouseDrawCursor flag
4563 if (g.IO.MouseDrawCursor)
4564 RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4565
4566 // Add foreground ImDrawList (for each active viewport)
4567 if (viewport->DrawLists[1] != NULL)
4568 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
4569
4570 SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
4571 ImDrawData* draw_data = &viewport->DrawDataP;
4572 g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
4573 g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
4574 }
4575
4576 CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4577 }
4578
4579 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4580 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)4581 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4582 {
4583 ImGuiContext& g = *GImGui;
4584
4585 const char* text_display_end;
4586 if (hide_text_after_double_hash)
4587 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4588 else
4589 text_display_end = text_end;
4590
4591 ImFont* font = g.Font;
4592 const float font_size = g.FontSize;
4593 if (text == text_display_end)
4594 return ImVec2(0.0f, font_size);
4595 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4596
4597 // Round
4598 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
4599 // FIXME: Investigate using ceilf or e.g.
4600 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
4601 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
4602 text_size.x = IM_FLOOR(text_size.x + 0.99999f);
4603
4604 return text_size;
4605 }
4606
4607 // Find window given position, search front-to-back
4608 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4609 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4610 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4611 static void FindHoveredWindow()
4612 {
4613 ImGuiContext& g = *GImGui;
4614
4615 ImGuiWindow* hovered_window = NULL;
4616 ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4617 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4618 hovered_window = g.MovingWindow;
4619
4620 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4621 ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
4622 for (int i = g.Windows.Size - 1; i >= 0; i--)
4623 {
4624 ImGuiWindow* window = g.Windows[i];
4625 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4626 if (!window->Active || window->Hidden)
4627 continue;
4628 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4629 continue;
4630
4631 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4632 ImRect bb(window->OuterRectClipped);
4633 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4634 bb.Expand(padding_regular);
4635 else
4636 bb.Expand(padding_for_resize);
4637 if (!bb.Contains(g.IO.MousePos))
4638 continue;
4639
4640 // Support for one rectangular hole in any given window
4641 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4642 if (window->HitTestHoleSize.x != 0)
4643 {
4644 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4645 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4646 if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4647 continue;
4648 }
4649
4650 if (hovered_window == NULL)
4651 hovered_window = window;
4652 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4653 if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4654 hovered_window_ignoring_moving_window = window;
4655 if (hovered_window && hovered_window_ignoring_moving_window)
4656 break;
4657 }
4658
4659 g.HoveredWindow = hovered_window;
4660 g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4661 }
4662
4663 // Test if mouse cursor is hovering given rectangle
4664 // NB- Rectangle is clipped by our current clip setting
4665 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)4666 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4667 {
4668 ImGuiContext& g = *GImGui;
4669
4670 // Clip
4671 ImRect rect_clipped(r_min, r_max);
4672 if (clip)
4673 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4674
4675 // Expand for touch input
4676 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4677 if (!rect_for_touch.Contains(g.IO.MousePos))
4678 return false;
4679 return true;
4680 }
4681
GetKeyIndex(ImGuiKey imgui_key)4682 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4683 {
4684 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4685 ImGuiContext& g = *GImGui;
4686 return g.IO.KeyMap[imgui_key];
4687 }
4688
4689 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4690 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4691 bool ImGui::IsKeyDown(int user_key_index)
4692 {
4693 if (user_key_index < 0)
4694 return false;
4695 ImGuiContext& g = *GImGui;
4696 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4697 return g.IO.KeysDown[user_key_index];
4698 }
4699
4700 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4701 // t1 = current time (e.g.: g.Time)
4702 // An event is triggered at:
4703 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4704 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4705 {
4706 if (t1 == 0.0f)
4707 return 1;
4708 if (t0 >= t1)
4709 return 0;
4710 if (repeat_rate <= 0.0f)
4711 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4712 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4713 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4714 const int count = count_t1 - count_t0;
4715 return count;
4716 }
4717
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4718 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4719 {
4720 ImGuiContext& g = *GImGui;
4721 if (key_index < 0)
4722 return 0;
4723 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4724 const float t = g.IO.KeysDownDuration[key_index];
4725 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4726 }
4727
IsKeyPressed(int user_key_index,bool repeat)4728 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4729 {
4730 ImGuiContext& g = *GImGui;
4731 if (user_key_index < 0)
4732 return false;
4733 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4734 const float t = g.IO.KeysDownDuration[user_key_index];
4735 if (t == 0.0f)
4736 return true;
4737 if (repeat && t > g.IO.KeyRepeatDelay)
4738 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4739 return false;
4740 }
4741
IsKeyReleased(int user_key_index)4742 bool ImGui::IsKeyReleased(int user_key_index)
4743 {
4744 ImGuiContext& g = *GImGui;
4745 if (user_key_index < 0) return false;
4746 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4747 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4748 }
4749
IsMouseDown(ImGuiMouseButton button)4750 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4751 {
4752 ImGuiContext& g = *GImGui;
4753 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4754 return g.IO.MouseDown[button];
4755 }
4756
IsMouseClicked(ImGuiMouseButton button,bool repeat)4757 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4758 {
4759 ImGuiContext& g = *GImGui;
4760 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4761 const float t = g.IO.MouseDownDuration[button];
4762 if (t == 0.0f)
4763 return true;
4764
4765 if (repeat && t > g.IO.KeyRepeatDelay)
4766 {
4767 // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold.
4768 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4769 if (amount > 0)
4770 return true;
4771 }
4772 return false;
4773 }
4774
IsMouseReleased(ImGuiMouseButton button)4775 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4776 {
4777 ImGuiContext& g = *GImGui;
4778 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4779 return g.IO.MouseReleased[button];
4780 }
4781
IsMouseDoubleClicked(ImGuiMouseButton button)4782 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4783 {
4784 ImGuiContext& g = *GImGui;
4785 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4786 return g.IO.MouseDoubleClicked[button];
4787 }
4788
4789 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4790 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4791 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4792 {
4793 ImGuiContext& g = *GImGui;
4794 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4795 if (lock_threshold < 0.0f)
4796 lock_threshold = g.IO.MouseDragThreshold;
4797 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4798 }
4799
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4800 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4801 {
4802 ImGuiContext& g = *GImGui;
4803 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4804 if (!g.IO.MouseDown[button])
4805 return false;
4806 return IsMouseDragPastThreshold(button, lock_threshold);
4807 }
4808
GetMousePos()4809 ImVec2 ImGui::GetMousePos()
4810 {
4811 ImGuiContext& g = *GImGui;
4812 return g.IO.MousePos;
4813 }
4814
4815 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4816 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4817 {
4818 ImGuiContext& g = *GImGui;
4819 if (g.BeginPopupStack.Size > 0)
4820 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4821 return g.IO.MousePos;
4822 }
4823
4824 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4825 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4826 {
4827 // The assert is only to silence a false-positive in XCode Static Analysis.
4828 // 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).
4829 IM_ASSERT(GImGui != NULL);
4830 const float MOUSE_INVALID = -256000.0f;
4831 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4832 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4833 }
4834
IsAnyMouseDown()4835 bool ImGui::IsAnyMouseDown()
4836 {
4837 ImGuiContext& g = *GImGui;
4838 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4839 if (g.IO.MouseDown[n])
4840 return true;
4841 return false;
4842 }
4843
4844 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4845 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4846 // NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4847 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4848 {
4849 ImGuiContext& g = *GImGui;
4850 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4851 if (lock_threshold < 0.0f)
4852 lock_threshold = g.IO.MouseDragThreshold;
4853 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4854 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4855 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4856 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4857 return ImVec2(0.0f, 0.0f);
4858 }
4859
ResetMouseDragDelta(ImGuiMouseButton button)4860 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4861 {
4862 ImGuiContext& g = *GImGui;
4863 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4864 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4865 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4866 }
4867
GetMouseCursor()4868 ImGuiMouseCursor ImGui::GetMouseCursor()
4869 {
4870 return GImGui->MouseCursor;
4871 }
4872
SetMouseCursor(ImGuiMouseCursor cursor_type)4873 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4874 {
4875 GImGui->MouseCursor = cursor_type;
4876 }
4877
CaptureKeyboardFromApp(bool capture)4878 void ImGui::CaptureKeyboardFromApp(bool capture)
4879 {
4880 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4881 }
4882
CaptureMouseFromApp(bool capture)4883 void ImGui::CaptureMouseFromApp(bool capture)
4884 {
4885 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4886 }
4887
IsItemActive()4888 bool ImGui::IsItemActive()
4889 {
4890 ImGuiContext& g = *GImGui;
4891 if (g.ActiveId)
4892 return g.ActiveId == g.LastItemData.ID;
4893 return false;
4894 }
4895
IsItemActivated()4896 bool ImGui::IsItemActivated()
4897 {
4898 ImGuiContext& g = *GImGui;
4899 if (g.ActiveId)
4900 if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
4901 return true;
4902 return false;
4903 }
4904
IsItemDeactivated()4905 bool ImGui::IsItemDeactivated()
4906 {
4907 ImGuiContext& g = *GImGui;
4908 if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4909 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4910 return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
4911 }
4912
IsItemDeactivatedAfterEdit()4913 bool ImGui::IsItemDeactivatedAfterEdit()
4914 {
4915 ImGuiContext& g = *GImGui;
4916 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4917 }
4918
4919 // == GetItemID() == GetFocusID()
IsItemFocused()4920 bool ImGui::IsItemFocused()
4921 {
4922 ImGuiContext& g = *GImGui;
4923 if (g.NavId != g.LastItemData.ID || g.NavId == 0)
4924 return false;
4925 return true;
4926 }
4927
4928 // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
4929 // Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
IsItemClicked(ImGuiMouseButton mouse_button)4930 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4931 {
4932 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4933 }
4934
IsItemToggledOpen()4935 bool ImGui::IsItemToggledOpen()
4936 {
4937 ImGuiContext& g = *GImGui;
4938 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4939 }
4940
IsItemToggledSelection()4941 bool ImGui::IsItemToggledSelection()
4942 {
4943 ImGuiContext& g = *GImGui;
4944 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4945 }
4946
IsAnyItemHovered()4947 bool ImGui::IsAnyItemHovered()
4948 {
4949 ImGuiContext& g = *GImGui;
4950 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4951 }
4952
IsAnyItemActive()4953 bool ImGui::IsAnyItemActive()
4954 {
4955 ImGuiContext& g = *GImGui;
4956 return g.ActiveId != 0;
4957 }
4958
IsAnyItemFocused()4959 bool ImGui::IsAnyItemFocused()
4960 {
4961 ImGuiContext& g = *GImGui;
4962 return g.NavId != 0 && !g.NavDisableHighlight;
4963 }
4964
IsItemVisible()4965 bool ImGui::IsItemVisible()
4966 {
4967 ImGuiContext& g = *GImGui;
4968 return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect);
4969 }
4970
IsItemEdited()4971 bool ImGui::IsItemEdited()
4972 {
4973 ImGuiContext& g = *GImGui;
4974 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4975 }
4976
4977 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4978 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
SetItemAllowOverlap()4979 void ImGui::SetItemAllowOverlap()
4980 {
4981 ImGuiContext& g = *GImGui;
4982 ImGuiID id = g.LastItemData.ID;
4983 if (g.HoveredId == id)
4984 g.HoveredIdAllowOverlap = true;
4985 if (g.ActiveId == id)
4986 g.ActiveIdAllowOverlap = true;
4987 }
4988
SetItemUsingMouseWheel()4989 void ImGui::SetItemUsingMouseWheel()
4990 {
4991 ImGuiContext& g = *GImGui;
4992 ImGuiID id = g.LastItemData.ID;
4993 if (g.HoveredId == id)
4994 g.HoveredIdUsingMouseWheel = true;
4995 if (g.ActiveId == id)
4996 g.ActiveIdUsingMouseWheel = true;
4997 }
4998
SetActiveIdUsingNavAndKeys()4999 void ImGui::SetActiveIdUsingNavAndKeys()
5000 {
5001 ImGuiContext& g = *GImGui;
5002 IM_ASSERT(g.ActiveId != 0);
5003 g.ActiveIdUsingNavDirMask = ~(ImU32)0;
5004 g.ActiveIdUsingNavInputMask = ~(ImU32)0;
5005 g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
5006 NavMoveRequestCancel();
5007 }
5008
GetItemRectMin()5009 ImVec2 ImGui::GetItemRectMin()
5010 {
5011 ImGuiContext& g = *GImGui;
5012 return g.LastItemData.Rect.Min;
5013 }
5014
GetItemRectMax()5015 ImVec2 ImGui::GetItemRectMax()
5016 {
5017 ImGuiContext& g = *GImGui;
5018 return g.LastItemData.Rect.Max;
5019 }
5020
GetItemRectSize()5021 ImVec2 ImGui::GetItemRectSize()
5022 {
5023 ImGuiContext& g = *GImGui;
5024 return g.LastItemData.Rect.GetSize();
5025 }
5026
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)5027 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
5028 {
5029 ImGuiContext& g = *GImGui;
5030 ImGuiWindow* parent_window = g.CurrentWindow;
5031
5032 flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
5033 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
5034
5035 // Size
5036 const ImVec2 content_avail = GetContentRegionAvail();
5037 ImVec2 size = ImFloor(size_arg);
5038 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5039 if (size.x <= 0.0f)
5040 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5041 if (size.y <= 0.0f)
5042 size.y = ImMax(content_avail.y + size.y, 4.0f);
5043 SetNextWindowSize(size);
5044
5045 // 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.
5046 if (name)
5047 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
5048 else
5049 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
5050
5051 const float backup_border_size = g.Style.ChildBorderSize;
5052 if (!border)
5053 g.Style.ChildBorderSize = 0.0f;
5054 bool ret = Begin(g.TempBuffer, NULL, flags);
5055 g.Style.ChildBorderSize = backup_border_size;
5056
5057 ImGuiWindow* child_window = g.CurrentWindow;
5058 child_window->ChildId = id;
5059 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
5060
5061 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5062 // While this is not really documented/defined, it seems that the expected thing to do.
5063 if (child_window->BeginCount == 1)
5064 parent_window->DC.CursorPos = child_window->Pos;
5065
5066 // Process navigation-in immediately so NavInit can run on first frame
5067 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll))
5068 {
5069 FocusWindow(child_window);
5070 NavInitWindow(child_window, false);
5071 SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5072 g.ActiveIdSource = ImGuiInputSource_Nav;
5073 }
5074 return ret;
5075 }
5076
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5077 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5078 {
5079 ImGuiWindow* window = GetCurrentWindow();
5080 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5081 }
5082
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)5083 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5084 {
5085 IM_ASSERT(id != 0);
5086 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5087 }
5088
EndChild()5089 void ImGui::EndChild()
5090 {
5091 ImGuiContext& g = *GImGui;
5092 ImGuiWindow* window = g.CurrentWindow;
5093
5094 IM_ASSERT(g.WithinEndChild == false);
5095 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
5096
5097 g.WithinEndChild = true;
5098 if (window->BeginCount > 1)
5099 {
5100 End();
5101 }
5102 else
5103 {
5104 ImVec2 sz = window->Size;
5105 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5106 sz.x = ImMax(4.0f, sz.x);
5107 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5108 sz.y = ImMax(4.0f, sz.y);
5109 End();
5110
5111 ImGuiWindow* parent_window = g.CurrentWindow;
5112 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5113 ItemSize(sz);
5114 if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5115 {
5116 ItemAdd(bb, window->ChildId);
5117 RenderNavHighlight(bb, window->ChildId);
5118
5119 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5120 if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
5121 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5122 }
5123 else
5124 {
5125 // Not navigable into
5126 ItemAdd(bb, 0);
5127 }
5128 if (g.HoveredWindow == window)
5129 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
5130 }
5131 g.WithinEndChild = false;
5132 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
5133 }
5134
5135 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)5136 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5137 {
5138 ImGuiContext& g = *GImGui;
5139 const ImGuiStyle& style = g.Style;
5140 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5141 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5142 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5143 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5144 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5145 PopStyleVar(3);
5146 PopStyleColor();
5147 return ret;
5148 }
5149
EndChildFrame()5150 void ImGui::EndChildFrame()
5151 {
5152 EndChild();
5153 }
5154
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)5155 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5156 {
5157 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
5158 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
5159 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5160 }
5161
FindWindowByID(ImGuiID id)5162 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5163 {
5164 ImGuiContext& g = *GImGui;
5165 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5166 }
5167
FindWindowByName(const char * name)5168 ImGuiWindow* ImGui::FindWindowByName(const char* name)
5169 {
5170 ImGuiID id = ImHashStr(name);
5171 return FindWindowByID(id);
5172 }
5173
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)5174 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5175 {
5176 window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
5177 if (settings->Size.x > 0 && settings->Size.y > 0)
5178 window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
5179 window->Collapsed = settings->Collapsed;
5180 }
5181
CreateNewWindow(const char * name,ImGuiWindowFlags flags)5182 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
5183 {
5184 ImGuiContext& g = *GImGui;
5185 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
5186
5187 // Create window the first time
5188 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5189 window->Flags = flags;
5190 g.WindowsById.SetVoidPtr(window->ID, window);
5191
5192 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5193 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5194 window->Pos = main_viewport->Pos + ImVec2(60, 60);
5195
5196 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5197 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5198 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5199 {
5200 // Retrieve settings from .ini file
5201 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
5202 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5203 ApplyWindowSettings(window, settings);
5204 }
5205 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
5206
5207 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5208 {
5209 window->AutoFitFramesX = window->AutoFitFramesY = 2;
5210 window->AutoFitOnlyGrows = false;
5211 }
5212 else
5213 {
5214 if (window->Size.x <= 0.0f)
5215 window->AutoFitFramesX = 2;
5216 if (window->Size.y <= 0.0f)
5217 window->AutoFitFramesY = 2;
5218 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5219 }
5220
5221 if (!(flags & ImGuiWindowFlags_ChildWindow))
5222 {
5223 g.WindowsFocusOrder.push_back(window);
5224 window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
5225 }
5226
5227 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5228 g.Windows.push_front(window); // Quite slow but rare and only once
5229 else
5230 g.Windows.push_back(window);
5231 return window;
5232 }
5233
CalcWindowSizeAfterConstraint(ImGuiWindow * window,const ImVec2 & size_desired)5234 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
5235 {
5236 ImGuiContext& g = *GImGui;
5237 ImVec2 new_size = size_desired;
5238 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5239 {
5240 // Using -1,-1 on either X/Y axis to preserve the current size.
5241 ImRect cr = g.NextWindowData.SizeConstraintRect;
5242 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5243 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5244 if (g.NextWindowData.SizeCallback)
5245 {
5246 ImGuiSizeCallbackData data;
5247 data.UserData = g.NextWindowData.SizeCallbackUserData;
5248 data.Pos = window->Pos;
5249 data.CurrentSize = window->SizeFull;
5250 data.DesiredSize = new_size;
5251 g.NextWindowData.SizeCallback(&data);
5252 new_size = data.DesiredSize;
5253 }
5254 new_size.x = IM_FLOOR(new_size.x);
5255 new_size.y = IM_FLOOR(new_size.y);
5256 }
5257
5258 // Minimum size
5259 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5260 {
5261 ImGuiWindow* window_for_height = window;
5262 const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight();
5263 new_size = ImMax(new_size, g.Style.WindowMinSize);
5264 new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5265 }
5266 return new_size;
5267 }
5268
CalcWindowContentSizes(ImGuiWindow * window,ImVec2 * content_size_current,ImVec2 * content_size_ideal)5269 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5270 {
5271 bool preserve_old_content_sizes = false;
5272 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5273 preserve_old_content_sizes = true;
5274 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5275 preserve_old_content_sizes = true;
5276 if (preserve_old_content_sizes)
5277 {
5278 *content_size_current = window->ContentSize;
5279 *content_size_ideal = window->ContentSizeIdeal;
5280 return;
5281 }
5282
5283 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5284 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5285 content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
5286 content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
5287 }
5288
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)5289 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5290 {
5291 ImGuiContext& g = *GImGui;
5292 ImGuiStyle& style = g.Style;
5293 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5294 ImVec2 size_pad = window->WindowPadding * 2.0f;
5295 ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height);
5296 if (window->Flags & ImGuiWindowFlags_Tooltip)
5297 {
5298 // Tooltip always resize
5299 return size_desired;
5300 }
5301 else
5302 {
5303 // Maximum window size is determined by the viewport size or monitor size
5304 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5305 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5306 ImVec2 size_min = style.WindowMinSize;
5307 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)
5308 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5309
5310 // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
5311 ImVec2 avail_size = ImGui::GetMainViewport()->Size;
5312 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));
5313
5314 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5315 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5316 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5317 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
5318 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
5319 if (will_have_scrollbar_x)
5320 size_auto_fit.y += style.ScrollbarSize;
5321 if (will_have_scrollbar_y)
5322 size_auto_fit.x += style.ScrollbarSize;
5323 return size_auto_fit;
5324 }
5325 }
5326
CalcWindowNextAutoFitSize(ImGuiWindow * window)5327 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5328 {
5329 ImVec2 size_contents_current;
5330 ImVec2 size_contents_ideal;
5331 CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5332 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5333 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5334 return size_final;
5335 }
5336
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5337 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5338 {
5339 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5340 return ImGuiCol_PopupBg;
5341 if (flags & ImGuiWindowFlags_ChildWindow)
5342 return ImGuiCol_ChildBg;
5343 return ImGuiCol_WindowBg;
5344 }
5345
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5346 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5347 {
5348 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
5349 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5350 ImVec2 size_expected = pos_max - pos_min;
5351 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5352 *out_pos = pos_min;
5353 if (corner_norm.x == 0.0f)
5354 out_pos->x -= (size_constrained.x - size_expected.x);
5355 if (corner_norm.y == 0.0f)
5356 out_pos->y -= (size_constrained.y - size_expected.y);
5357 *out_size = size_constrained;
5358 }
5359
5360 // Data for resizing from corner
5361 struct ImGuiResizeGripDef
5362 {
5363 ImVec2 CornerPosN;
5364 ImVec2 InnerDir;
5365 int AngleMin12, AngleMax12;
5366 };
5367 static const ImGuiResizeGripDef resize_grip_def[4] =
5368 {
5369 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5370 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5371 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5372 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
5373 };
5374
5375 // Data for resizing from borders
5376 struct ImGuiResizeBorderDef
5377 {
5378 ImVec2 InnerDir;
5379 ImVec2 SegmentN1, SegmentN2;
5380 float OuterAngle;
5381 };
5382 static const ImGuiResizeBorderDef resize_border_def[4] =
5383 {
5384 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
5385 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5386 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
5387 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down
5388 };
5389
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5390 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5391 {
5392 ImRect rect = window->Rect();
5393 if (thickness == 0.0f)
5394 rect.Max -= ImVec2(1, 1);
5395 if (border_n == ImGuiDir_Left) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); }
5396 if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); }
5397 if (border_n == ImGuiDir_Up) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); }
5398 if (border_n == ImGuiDir_Down) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); }
5399 IM_ASSERT(0);
5400 return ImRect();
5401 }
5402
5403 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
GetWindowResizeCornerID(ImGuiWindow * window,int n)5404 ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
5405 {
5406 IM_ASSERT(n >= 0 && n < 4);
5407 ImGuiID id = window->ID;
5408 id = ImHashStr("#RESIZE", 0, id);
5409 id = ImHashData(&n, sizeof(int), id);
5410 return id;
5411 }
5412
5413 // Borders (Left, Right, Up, Down)
GetWindowResizeBorderID(ImGuiWindow * window,ImGuiDir dir)5414 ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
5415 {
5416 IM_ASSERT(dir >= 0 && dir < 4);
5417 int n = (int)dir + 4;
5418 ImGuiID id = window->ID;
5419 id = ImHashStr("#RESIZE", 0, id);
5420 id = ImHashData(&n, sizeof(int), id);
5421 return id;
5422 }
5423
5424 // Handle resize for: Resize Grips, Borders, Gamepad
5425 // Return true when using auto-fit (double click on resize grip)
UpdateWindowManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4],const ImRect & visibility_rect)5426 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
5427 {
5428 ImGuiContext& g = *GImGui;
5429 ImGuiWindowFlags flags = window->Flags;
5430
5431 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5432 return false;
5433 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5434 return false;
5435
5436 bool ret_auto_fit = false;
5437 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5438 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5439 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5440 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
5441
5442 ImVec2 pos_target(FLT_MAX, FLT_MAX);
5443 ImVec2 size_target(FLT_MAX, FLT_MAX);
5444
5445 // Resize grips and borders are on layer 1
5446 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5447
5448 // Manual resize grips
5449 PushID("#RESIZE");
5450 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5451 {
5452 const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
5453 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
5454
5455 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5456 bool hovered, held;
5457 ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
5458 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5459 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5460 ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
5461 ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5462 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5463 if (hovered || held)
5464 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5465
5466 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5467 {
5468 // Manual auto-fit when double-clicking
5469 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5470 ret_auto_fit = true;
5471 ClearActiveID();
5472 }
5473 else if (held)
5474 {
5475 // Resize from any of the four corners
5476 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5477 ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5478 ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5479 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
5480 corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5481 CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
5482 }
5483
5484 // Only lower-left grip is visible before hovering/activating
5485 if (resize_grip_n == 0 || held || hovered)
5486 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5487 }
5488 for (int border_n = 0; border_n < resize_border_count; border_n++)
5489 {
5490 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
5491 const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
5492
5493 bool hovered, held;
5494 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
5495 ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
5496 ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5497 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5498 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5499 {
5500 g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5501 if (held)
5502 *border_held = border_n;
5503 }
5504 if (held)
5505 {
5506 ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX);
5507 ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX);
5508 ImVec2 border_target = window->Pos;
5509 border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING;
5510 border_target = ImClamp(border_target, clamp_min, clamp_max);
5511 CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
5512 }
5513 }
5514 PopID();
5515
5516 // Restore nav layer
5517 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5518
5519 // Navigation resize (keyboard/gamepad)
5520 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5521 {
5522 ImVec2 nav_resize_delta;
5523 if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
5524 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5525 if (g.NavInputSource == ImGuiInputSource_Gamepad)
5526 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5527 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5528 {
5529 const float NAV_RESIZE_SPEED = 600.0f;
5530 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5531 nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5532 g.NavWindowingToggleLayer = false;
5533 g.NavDisableMouseHover = true;
5534 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5535 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5536 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5537 }
5538 }
5539
5540 // Apply back modified position/size to window
5541 if (size_target.x != FLT_MAX)
5542 {
5543 window->SizeFull = size_target;
5544 MarkIniSettingsDirty(window);
5545 }
5546 if (pos_target.x != FLT_MAX)
5547 {
5548 window->Pos = ImFloor(pos_target);
5549 MarkIniSettingsDirty(window);
5550 }
5551
5552 window->Size = window->SizeFull;
5553 return ret_auto_fit;
5554 }
5555
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5556 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5557 {
5558 ImGuiContext& g = *GImGui;
5559 ImVec2 size_for_clamping = window->Size;
5560 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5561 size_for_clamping.y = window->TitleBarHeight();
5562 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5563 }
5564
RenderWindowOuterBorders(ImGuiWindow * window)5565 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5566 {
5567 ImGuiContext& g = *GImGui;
5568 float rounding = window->WindowRounding;
5569 float border_size = window->WindowBorderSize;
5570 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5571 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
5572
5573 int border_held = window->ResizeBorderHeld;
5574 if (border_held != -1)
5575 {
5576 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5577 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5578 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
5579 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
5580 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual
5581 }
5582 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5583 {
5584 float y = window->Pos.y + window->TitleBarHeight() - 1;
5585 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);
5586 }
5587 }
5588
5589 // Draw background and borders
5590 // Draw and handle scrollbars
RenderWindowDecorations(ImGuiWindow * window,const ImRect & title_bar_rect,bool title_bar_is_highlight,int resize_grip_count,const ImU32 resize_grip_col[4],float resize_grip_draw_size)5591 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
5592 {
5593 ImGuiContext& g = *GImGui;
5594 ImGuiStyle& style = g.Style;
5595 ImGuiWindowFlags flags = window->Flags;
5596
5597 // Ensure that ScrollBar doesn't read last frame's SkipItems
5598 IM_ASSERT(window->BeginCount == 0);
5599 window->SkipItems = false;
5600
5601 // Draw window + handle manual resize
5602 // 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.
5603 const float window_rounding = window->WindowRounding;
5604 const float window_border_size = window->WindowBorderSize;
5605 if (window->Collapsed)
5606 {
5607 // Title bar only
5608 float backup_border_size = style.FrameBorderSize;
5609 g.Style.FrameBorderSize = window->WindowBorderSize;
5610 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5611 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5612 g.Style.FrameBorderSize = backup_border_size;
5613 }
5614 else
5615 {
5616 // Window background
5617 if (!(flags & ImGuiWindowFlags_NoBackground))
5618 {
5619 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5620 bool override_alpha = false;
5621 float alpha = 1.0f;
5622 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5623 {
5624 alpha = g.NextWindowData.BgAlphaVal;
5625 override_alpha = true;
5626 }
5627 if (override_alpha)
5628 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5629 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
5630 }
5631
5632 // Title bar
5633 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5634 {
5635 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5636 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
5637 }
5638
5639 // Menu bar
5640 if (flags & ImGuiWindowFlags_MenuBar)
5641 {
5642 ImRect menu_bar_rect = window->MenuBarRect();
5643 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.
5644 window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
5645 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5646 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5647 }
5648
5649 // Scrollbars
5650 if (window->ScrollbarX)
5651 Scrollbar(ImGuiAxis_X);
5652 if (window->ScrollbarY)
5653 Scrollbar(ImGuiAxis_Y);
5654
5655 // Render resize grips (after their input handling so we don't have a frame of latency)
5656 if (!(flags & ImGuiWindowFlags_NoResize))
5657 {
5658 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5659 {
5660 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5661 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5662 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
5663 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
5664 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);
5665 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5666 }
5667 }
5668
5669 // Borders
5670 RenderWindowOuterBorders(window);
5671 }
5672 }
5673
5674 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5675 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5676 {
5677 ImGuiContext& g = *GImGui;
5678 ImGuiStyle& style = g.Style;
5679 ImGuiWindowFlags flags = window->Flags;
5680
5681 const bool has_close_button = (p_open != NULL);
5682 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5683
5684 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5685 const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
5686 g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5687 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5688
5689 // Layout buttons
5690 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5691 float pad_l = style.FramePadding.x;
5692 float pad_r = style.FramePadding.x;
5693 float button_sz = g.FontSize;
5694 ImVec2 close_button_pos;
5695 ImVec2 collapse_button_pos;
5696 if (has_close_button)
5697 {
5698 pad_r += button_sz;
5699 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5700 }
5701 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5702 {
5703 pad_r += button_sz;
5704 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5705 }
5706 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5707 {
5708 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5709 pad_l += button_sz;
5710 }
5711
5712 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5713 if (has_collapse_button)
5714 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5715 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5716
5717 // Close button
5718 if (has_close_button)
5719 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5720 *p_open = false;
5721
5722 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5723 g.CurrentItemFlags = item_flags_backup;
5724
5725 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5726 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5727 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
5728 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5729
5730 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5731 // while uncentered title text will still reach edges correctly.
5732 if (pad_l > style.FramePadding.x)
5733 pad_l += g.Style.ItemInnerSpacing.x;
5734 if (pad_r > style.FramePadding.x)
5735 pad_r += g.Style.ItemInnerSpacing.x;
5736 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5737 {
5738 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5739 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5740 pad_l = ImMax(pad_l, pad_extend * centerness);
5741 pad_r = ImMax(pad_r, pad_extend * centerness);
5742 }
5743
5744 ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);
5745 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
5746 if (flags & ImGuiWindowFlags_UnsavedDocument)
5747 {
5748 ImVec2 marker_pos;
5749 marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
5750 marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
5751 if (marker_pos.x > layout_r.Min.x)
5752 {
5753 RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
5754 clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
5755 }
5756 }
5757 //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5758 //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5759 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5760 }
5761
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5762 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5763 {
5764 window->ParentWindow = parent_window;
5765 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5766 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5767 window->RootWindow = parent_window->RootWindow;
5768 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5769 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5770 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5771 {
5772 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5773 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5774 }
5775 }
5776
5777 // Push a new Dear ImGui window to add widgets to.
5778 // - 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.
5779 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5780 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5781 // 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.
5782 // - 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.
5783 // - 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.
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)5784 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5785 {
5786 ImGuiContext& g = *GImGui;
5787 const ImGuiStyle& style = g.Style;
5788 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5789 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
5790 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5791
5792 // Find or create
5793 ImGuiWindow* window = FindWindowByName(name);
5794 const bool window_just_created = (window == NULL);
5795 if (window_just_created)
5796 window = CreateNewWindow(name, flags);
5797
5798 // Automatically disable manual moving/resizing when NoInputs is set
5799 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5800 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5801
5802 if (flags & ImGuiWindowFlags_NavFlattened)
5803 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5804
5805 const int current_frame = g.FrameCount;
5806 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5807 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5808
5809 // Update the Appearing flag
5810 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5811 if (flags & ImGuiWindowFlags_Popup)
5812 {
5813 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5814 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5815 window_just_activated_by_user |= (window != popup_ref.Window);
5816 }
5817 window->Appearing = window_just_activated_by_user;
5818 if (window->Appearing)
5819 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5820
5821 // Update Flags, LastFrameActive, BeginOrderXXX fields
5822 if (first_begin_of_the_frame)
5823 {
5824 window->Flags = (ImGuiWindowFlags)flags;
5825 window->LastFrameActive = current_frame;
5826 window->LastTimeActive = (float)g.Time;
5827 window->BeginOrderWithinParent = 0;
5828 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5829 }
5830 else
5831 {
5832 flags = window->Flags;
5833 }
5834
5835 // 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
5836 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
5837 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5838 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5839
5840 // We allow window memory to be compacted so recreate the base stack when needed.
5841 if (window->IDStack.Size == 0)
5842 window->IDStack.push_back(window->ID);
5843
5844 // Add to stack
5845 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5846 ImGuiWindowStackData window_stack_data;
5847 window_stack_data.Window = window;
5848 window_stack_data.ParentLastItemDataBackup = g.LastItemData;
5849 g.CurrentWindowStack.push_back(window_stack_data);
5850 g.CurrentWindow = window;
5851 window->DC.StackSizesOnBegin.SetToCurrentState();
5852 g.CurrentWindow = NULL;
5853
5854 if (flags & ImGuiWindowFlags_Popup)
5855 {
5856 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5857 popup_ref.Window = window;
5858 g.BeginPopupStack.push_back(popup_ref);
5859 window->PopupId = popup_ref.PopupId;
5860 }
5861
5862 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5863 if (first_begin_of_the_frame)
5864 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5865
5866 // Process SetNextWindow***() calls
5867 // (FIXME: Consider splitting the HasXXX flags into X/Y components
5868 bool window_pos_set_by_api = false;
5869 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5870 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5871 {
5872 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5873 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5874 {
5875 // May be processed on the next frame if this is our first frame and we are measuring size
5876 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5877 window->SetWindowPosVal = g.NextWindowData.PosVal;
5878 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5879 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5880 }
5881 else
5882 {
5883 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5884 }
5885 }
5886 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5887 {
5888 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5889 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5890 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5891 }
5892 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5893 {
5894 if (g.NextWindowData.ScrollVal.x >= 0.0f)
5895 {
5896 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5897 window->ScrollTargetCenterRatio.x = 0.0f;
5898 }
5899 if (g.NextWindowData.ScrollVal.y >= 0.0f)
5900 {
5901 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5902 window->ScrollTargetCenterRatio.y = 0.0f;
5903 }
5904 }
5905 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5906 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5907 else if (first_begin_of_the_frame)
5908 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5909 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5910 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5911 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5912 FocusWindow(window);
5913 if (window->Appearing)
5914 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5915
5916 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5917 if (first_begin_of_the_frame)
5918 {
5919 // Initialize
5920 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5921 window->Active = true;
5922 window->HasCloseButton = (p_open != NULL);
5923 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5924 window->IDStack.resize(1);
5925 window->DrawList->_ResetForNewFrame();
5926 window->DC.CurrentTableIdx = -1;
5927
5928 // Restore buffer capacity when woken from a compacted state, to avoid
5929 if (window->MemoryCompacted)
5930 GcAwakeTransientWindowBuffers(window);
5931
5932 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5933 // 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.
5934 bool window_title_visible_elsewhere = false;
5935 if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5936 window_title_visible_elsewhere = true;
5937 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5938 {
5939 size_t buf_len = (size_t)window->NameBufLen;
5940 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5941 window->NameBufLen = (int)buf_len;
5942 }
5943
5944 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5945
5946 // Update contents size from last frame for auto-fitting (or use explicit size)
5947 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5948 CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
5949 if (window->HiddenFramesCanSkipItems > 0)
5950 window->HiddenFramesCanSkipItems--;
5951 if (window->HiddenFramesCannotSkipItems > 0)
5952 window->HiddenFramesCannotSkipItems--;
5953 if (window->HiddenFramesForRenderOnly > 0)
5954 window->HiddenFramesForRenderOnly--;
5955
5956 // Hide new windows for one frame until they calculate their size
5957 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5958 window->HiddenFramesCannotSkipItems = 1;
5959
5960 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5961 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5962 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5963 {
5964 window->HiddenFramesCannotSkipItems = 1;
5965 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5966 {
5967 if (!window_size_x_set_by_api)
5968 window->Size.x = window->SizeFull.x = 0.f;
5969 if (!window_size_y_set_by_api)
5970 window->Size.y = window->SizeFull.y = 0.f;
5971 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
5972 }
5973 }
5974
5975 // SELECT VIEWPORT
5976 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5977 SetCurrentWindow(window);
5978
5979 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5980
5981 if (flags & ImGuiWindowFlags_ChildWindow)
5982 window->WindowBorderSize = style.ChildBorderSize;
5983 else
5984 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5985 window->WindowPadding = style.WindowPadding;
5986 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5987 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5988
5989 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
5990 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5991 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5992
5993 // Collapse window by double-clicking on title bar
5994 // 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
5995 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5996 {
5997 // 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.
5998 ImRect title_bar_rect = window->TitleBarRect();
5999 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
6000 window->WantCollapseToggle = true;
6001 if (window->WantCollapseToggle)
6002 {
6003 window->Collapsed = !window->Collapsed;
6004 MarkIniSettingsDirty(window);
6005 }
6006 }
6007 else
6008 {
6009 window->Collapsed = false;
6010 }
6011 window->WantCollapseToggle = false;
6012
6013 // SIZE
6014
6015 // Calculate auto-fit size, handle automatic resize
6016 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
6017 bool use_current_size_for_scrollbar_x = window_just_created;
6018 bool use_current_size_for_scrollbar_y = window_just_created;
6019 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
6020 {
6021 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
6022 if (!window_size_x_set_by_api)
6023 {
6024 window->SizeFull.x = size_auto_fit.x;
6025 use_current_size_for_scrollbar_x = true;
6026 }
6027 if (!window_size_y_set_by_api)
6028 {
6029 window->SizeFull.y = size_auto_fit.y;
6030 use_current_size_for_scrollbar_y = true;
6031 }
6032 }
6033 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6034 {
6035 // Auto-fit may only grow window during the first few frames
6036 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
6037 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
6038 {
6039 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
6040 use_current_size_for_scrollbar_x = true;
6041 }
6042 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
6043 {
6044 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
6045 use_current_size_for_scrollbar_y = true;
6046 }
6047 if (!window->Collapsed)
6048 MarkIniSettingsDirty(window);
6049 }
6050
6051 // Apply minimum/maximum window size constraints and final size
6052 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
6053 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
6054
6055 // Decoration size
6056 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
6057
6058 // POSITION
6059
6060 // Popup latch its initial position, will position itself when it appears next frame
6061 if (window_just_activated_by_user)
6062 {
6063 window->AutoPosLastDirection = ImGuiDir_None;
6064 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
6065 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
6066 }
6067
6068 // Position child window
6069 if (flags & ImGuiWindowFlags_ChildWindow)
6070 {
6071 IM_ASSERT(parent_window && parent_window->Active);
6072 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
6073 parent_window->DC.ChildWindows.push_back(window);
6074 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
6075 window->Pos = parent_window->DC.CursorPos;
6076 }
6077
6078 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
6079 if (window_pos_with_pivot)
6080 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
6081 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
6082 window->Pos = FindBestWindowPosForPopup(window);
6083 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
6084 window->Pos = FindBestWindowPosForPopup(window);
6085 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6086 window->Pos = FindBestWindowPosForPopup(window);
6087
6088 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
6089 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
6090 ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
6091 ImRect viewport_rect(viewport->GetMainRect());
6092 ImRect viewport_work_rect(viewport->GetWorkRect());
6093 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6094 ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
6095
6096 // Clamp position/size so window stays visible within its viewport or monitor
6097 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
6098 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6099 if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
6100 ClampWindowRect(window, visibility_rect);
6101 window->Pos = ImFloor(window->Pos);
6102
6103 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6104 // Large values tend to lead to variety of artifacts and are not recommended.
6105 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
6106
6107 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
6108 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6109 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
6110
6111 // Apply window focus (new and reactivated windows are moved to front)
6112 bool want_focus = false;
6113 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6114 {
6115 if (flags & ImGuiWindowFlags_Popup)
6116 want_focus = true;
6117 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
6118 want_focus = true;
6119 }
6120
6121 // Handle manual resize: Resize Grips, Borders, Gamepad
6122 int border_held = -1;
6123 ImU32 resize_grip_col[4] = {};
6124 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
6125 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6126 if (!window->Collapsed)
6127 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
6128 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
6129 window->ResizeBorderHeld = (signed char)border_held;
6130
6131 // SCROLLBAR VISIBILITY
6132
6133 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
6134 if (!window->Collapsed)
6135 {
6136 // When reading the current size we need to read it after size constraints have been applied.
6137 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
6138 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
6139 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
6140 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
6141 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
6142 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
6143 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
6144 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
6145 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
6146 if (window->ScrollbarX && !window->ScrollbarY)
6147 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
6148 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
6149 }
6150
6151 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
6152 // Update various regions. Variables they depends on should be set above in this function.
6153 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
6154
6155 // Outer rectangle
6156 // Not affected by window border size. Used by:
6157 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
6158 // - Begin() initial clipping rect for drawing window background and borders.
6159 // - Begin() clipping whole child
6160 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
6161 const ImRect outer_rect = window->Rect();
6162 const ImRect title_bar_rect = window->TitleBarRect();
6163 window->OuterRectClipped = outer_rect;
6164 window->OuterRectClipped.ClipWith(host_rect);
6165
6166 // Inner rectangle
6167 // Not affected by window border size. Used by:
6168 // - InnerClipRect
6169 // - ScrollToBringRectIntoView()
6170 // - NavUpdatePageUpPageDown()
6171 // - Scrollbar()
6172 window->InnerRect.Min.x = window->Pos.x;
6173 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
6174 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
6175 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
6176
6177 // Inner clipping rectangle.
6178 // Will extend a little bit outside the normal work region.
6179 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
6180 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6181 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6182 // Affected by window/frame border size. Used by:
6183 // - Begin() initial clip rect
6184 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6185 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6186 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
6187 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6188 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
6189 window->InnerClipRect.ClipWithFull(host_rect);
6190
6191 // Default item width. Make it proportional to window size if window manually resizes
6192 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6193 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
6194 else
6195 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
6196
6197 // SCROLLING
6198
6199 // Lock down maximum scrolling
6200 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
6201 // for right/bottom aligned items without creating a scrollbar.
6202 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
6203 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
6204
6205 // Apply scrolling
6206 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6207 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6208
6209 // DRAWING
6210
6211 // Setup draw list and outer clipping rectangle
6212 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
6213 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6214 PushClipRect(host_rect.Min, host_rect.Max, false);
6215
6216 // Draw modal window background (darkens what is behind them, all viewports)
6217 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6218 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
6219 if (dim_bg_for_modal || dim_bg_for_window_list)
6220 {
6221 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6222 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6223 }
6224
6225 // Draw navigation selection/windowing rectangle background
6226 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
6227 {
6228 ImRect bb = window->Rect();
6229 bb.Expand(g.FontSize);
6230 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
6231 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6232 }
6233
6234 // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
6235 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
6236 // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
6237 {
6238 bool render_decorations_in_parent = false;
6239 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6240 {
6241 // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
6242 // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
6243 ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
6244 bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
6245 bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0;
6246 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping)
6247 render_decorations_in_parent = true;
6248 }
6249 if (render_decorations_in_parent)
6250 window->DrawList = parent_window->DrawList;
6251
6252 // Handle title bar, scrollbar, resize grips and resize borders
6253 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6254 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
6255 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6256
6257 if (render_decorations_in_parent)
6258 window->DrawList = &window->DrawListInst;
6259 }
6260
6261 // Draw navigation selection/windowing rectangle border
6262 if (g.NavWindowingTargetAnim == window)
6263 {
6264 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6265 ImRect bb = window->Rect();
6266 bb.Expand(g.FontSize);
6267 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6268 {
6269 bb.Expand(-g.FontSize - 1.0f);
6270 rounding = window->WindowRounding;
6271 }
6272 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f);
6273 }
6274
6275 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6276
6277 // Work rectangle.
6278 // Affected by window padding and border size. Used by:
6279 // - Columns() for right-most edge
6280 // - TreeNode(), CollapsingHeader() for right-most edge
6281 // - BeginTabBar() for right-most edge
6282 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6283 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6284 const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x));
6285 const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y));
6286 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6287 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6288 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6289 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6290 window->ParentWorkRect = window->WorkRect;
6291
6292 // [LEGACY] Content Region
6293 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6294 // Used by:
6295 // - Mouse wheel scrolling + many other things
6296 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6297 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6298 window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x));
6299 window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y));
6300
6301 // Setup drawing context
6302 // (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.)
6303 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6304 window->DC.GroupOffset.x = 0.0f;
6305 window->DC.ColumnsOffset.x = 0.0f;
6306 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6307 window->DC.CursorPos = window->DC.CursorStartPos;
6308 window->DC.CursorPosPrevLine = window->DC.CursorPos;
6309 window->DC.CursorMaxPos = window->DC.CursorStartPos;
6310 window->DC.IdealMaxPos = window->DC.CursorStartPos;
6311 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6312 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6313
6314 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6315 window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
6316 window->DC.NavLayersActiveMaskNext = 0x00;
6317 window->DC.NavHideHighlightOneFrame = false;
6318 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6319
6320 window->DC.MenuBarAppending = false;
6321 window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
6322 window->DC.TreeDepth = 0;
6323 window->DC.TreeJumpToParentOnPopMask = 0x00;
6324 window->DC.ChildWindows.resize(0);
6325 window->DC.StateStorage = &window->StateStorage;
6326 window->DC.CurrentColumns = NULL;
6327 window->DC.LayoutType = ImGuiLayoutType_Vertical;
6328 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6329 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6330
6331 window->DC.ItemWidth = window->ItemWidthDefault;
6332 window->DC.TextWrapPos = -1.0f; // disabled
6333 window->DC.ItemWidthStack.resize(0);
6334 window->DC.TextWrapPosStack.resize(0);
6335
6336 if (window->AutoFitFramesX > 0)
6337 window->AutoFitFramesX--;
6338 if (window->AutoFitFramesY > 0)
6339 window->AutoFitFramesY--;
6340
6341 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6342 if (want_focus)
6343 {
6344 FocusWindow(window);
6345 NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
6346 }
6347
6348 // Title bar
6349 if (!(flags & ImGuiWindowFlags_NoTitleBar))
6350 RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
6351
6352 // Clear hit test shape every frame
6353 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6354
6355 // Pressing CTRL+C while holding on a window copy its content to the clipboard
6356 // 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.
6357 // Maybe we can support CTRL+C on every element?
6358 /*
6359 //if (g.NavWindow == window && g.ActiveId == 0)
6360 if (g.ActiveId == window->MoveId)
6361 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6362 LogToClipboard();
6363 */
6364
6365 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6366 // This is useful to allow creating context menus on title bar only, etc.
6367 g.LastItemData.ID = window->MoveId;
6368 g.LastItemData.InFlags = g.CurrentItemFlags;
6369 g.LastItemData.StatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
6370 g.LastItemData.Rect = title_bar_rect;
6371
6372 #ifdef IMGUI_ENABLE_TEST_ENGINE
6373 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6374 IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID);
6375 #endif
6376 }
6377 else
6378 {
6379 // Append
6380 SetCurrentWindow(window);
6381 }
6382
6383 // Pull/inherit current state
6384 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595
6385
6386 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6387
6388 // 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)
6389 window->WriteAccessed = false;
6390 window->BeginCount++;
6391 g.NextWindowData.ClearFlags();
6392
6393 // Update visibility
6394 if (first_begin_of_the_frame)
6395 {
6396 if (flags & ImGuiWindowFlags_ChildWindow)
6397 {
6398 // Child window can be out of sight and have "negative" clip windows.
6399 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6400 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6401 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6402 if (!g.LogEnabled)
6403 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6404 window->HiddenFramesCanSkipItems = 1;
6405
6406 // Hide along with parent or if parent is collapsed
6407 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6408 window->HiddenFramesCanSkipItems = 1;
6409 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6410 window->HiddenFramesCannotSkipItems = 1;
6411 }
6412
6413 // 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)
6414 if (style.Alpha <= 0.0f)
6415 window->HiddenFramesCanSkipItems = 1;
6416
6417 // Update the Hidden flag
6418 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6419
6420 // Disable inputs for requested number of frames
6421 if (window->DisableInputsFrames > 0)
6422 {
6423 window->DisableInputsFrames--;
6424 window->Flags |= ImGuiWindowFlags_NoInputs;
6425 }
6426
6427 // Update the SkipItems flag, used to early out of all items functions (no layout required)
6428 bool skip_items = false;
6429 if (window->Collapsed || !window->Active || window->Hidden)
6430 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6431 skip_items = true;
6432 window->SkipItems = skip_items;
6433 }
6434
6435 return !window->SkipItems;
6436 }
6437
End()6438 void ImGui::End()
6439 {
6440 ImGuiContext& g = *GImGui;
6441 ImGuiWindow* window = g.CurrentWindow;
6442
6443 // Error checking: verify that user hasn't called End() too many times!
6444 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6445 {
6446 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6447 return;
6448 }
6449 IM_ASSERT(g.CurrentWindowStack.Size > 0);
6450
6451 // Error checking: verify that user doesn't directly call End() on a child window.
6452 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6453 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6454
6455 // Close anything that is open
6456 if (window->DC.CurrentColumns)
6457 EndColumns();
6458 PopClipRect(); // Inner window clip rectangle
6459
6460 // Stop logging
6461 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
6462 LogFinish();
6463
6464 // Pop from window stack
6465 g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
6466 g.CurrentWindowStack.pop_back();
6467 if (window->Flags & ImGuiWindowFlags_Popup)
6468 g.BeginPopupStack.pop_back();
6469 window->DC.StackSizesOnBegin.CompareWithCurrentState();
6470 SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
6471 }
6472
BringWindowToFocusFront(ImGuiWindow * window)6473 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6474 {
6475 ImGuiContext& g = *GImGui;
6476 IM_ASSERT(window == window->RootWindow);
6477
6478 const int cur_order = window->FocusOrder;
6479 IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
6480 if (g.WindowsFocusOrder.back() == window)
6481 return;
6482
6483 const int new_order = g.WindowsFocusOrder.Size - 1;
6484 for (int n = cur_order; n < new_order; n++)
6485 {
6486 g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
6487 g.WindowsFocusOrder[n]->FocusOrder--;
6488 IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
6489 }
6490 g.WindowsFocusOrder[new_order] = window;
6491 window->FocusOrder = (short)new_order;
6492 }
6493
BringWindowToDisplayFront(ImGuiWindow * window)6494 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6495 {
6496 ImGuiContext& g = *GImGui;
6497 ImGuiWindow* current_front_window = g.Windows.back();
6498 if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6499 return;
6500 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6501 if (g.Windows[i] == window)
6502 {
6503 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6504 g.Windows[g.Windows.Size - 1] = window;
6505 break;
6506 }
6507 }
6508
BringWindowToDisplayBack(ImGuiWindow * window)6509 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6510 {
6511 ImGuiContext& g = *GImGui;
6512 if (g.Windows[0] == window)
6513 return;
6514 for (int i = 0; i < g.Windows.Size; i++)
6515 if (g.Windows[i] == window)
6516 {
6517 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6518 g.Windows[0] = window;
6519 break;
6520 }
6521 }
6522
6523 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6524 void ImGui::FocusWindow(ImGuiWindow* window)
6525 {
6526 ImGuiContext& g = *GImGui;
6527
6528 if (g.NavWindow != window)
6529 {
6530 g.NavWindow = window;
6531 if (window && g.NavDisableMouseHover)
6532 g.NavMousePosDirty = true;
6533 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6534 g.NavFocusScopeId = 0;
6535 g.NavIdIsAlive = false;
6536 g.NavLayer = ImGuiNavLayer_Main;
6537 g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
6538 NavUpdateAnyRequestFlag();
6539 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6540 }
6541
6542 // Close popups if any
6543 ClosePopupsOverWindow(window, false);
6544
6545 // Move the root window to the top of the pile
6546 IM_ASSERT(window == NULL || window->RootWindow != NULL);
6547 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
6548 ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6549
6550 // Steal active widgets. Some of the cases it triggers includes:
6551 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6552 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6553 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6554 if (!g.ActiveIdNoClearOnFocusLoss)
6555 ClearActiveID();
6556
6557 // Passing NULL allow to disable keyboard focus
6558 if (!window)
6559 return;
6560
6561 // Bring to front
6562 BringWindowToFocusFront(focus_front_window);
6563 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6564 BringWindowToDisplayFront(display_front_window);
6565 }
6566
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6567 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6568 {
6569 ImGuiContext& g = *GImGui;
6570
6571 const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1;
6572 for (int i = start_idx; i >= 0; i--)
6573 {
6574 // 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.
6575 ImGuiWindow* window = g.WindowsFocusOrder[i];
6576 IM_ASSERT(window == window->RootWindow);
6577 if (window != ignore_window && window->WasActive)
6578 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6579 {
6580 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6581 FocusWindow(focus_window);
6582 return;
6583 }
6584 }
6585 FocusWindow(NULL);
6586 }
6587
6588 // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
SetCurrentFont(ImFont * font)6589 void ImGui::SetCurrentFont(ImFont* font)
6590 {
6591 ImGuiContext& g = *GImGui;
6592 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6593 IM_ASSERT(font->Scale > 0.0f);
6594 g.Font = font;
6595 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6596 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6597
6598 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6599 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6600 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6601 g.DrawListSharedData.Font = g.Font;
6602 g.DrawListSharedData.FontSize = g.FontSize;
6603 }
6604
PushFont(ImFont * font)6605 void ImGui::PushFont(ImFont* font)
6606 {
6607 ImGuiContext& g = *GImGui;
6608 if (!font)
6609 font = GetDefaultFont();
6610 SetCurrentFont(font);
6611 g.FontStack.push_back(font);
6612 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6613 }
6614
PopFont()6615 void ImGui::PopFont()
6616 {
6617 ImGuiContext& g = *GImGui;
6618 g.CurrentWindow->DrawList->PopTextureID();
6619 g.FontStack.pop_back();
6620 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6621 }
6622
PushItemFlag(ImGuiItemFlags option,bool enabled)6623 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6624 {
6625 ImGuiContext& g = *GImGui;
6626 ImGuiItemFlags item_flags = g.CurrentItemFlags;
6627 IM_ASSERT(item_flags == g.ItemFlagsStack.back());
6628 if (enabled)
6629 item_flags |= option;
6630 else
6631 item_flags &= ~option;
6632 g.CurrentItemFlags = item_flags;
6633 g.ItemFlagsStack.push_back(item_flags);
6634 }
6635
PopItemFlag()6636 void ImGui::PopItemFlag()
6637 {
6638 ImGuiContext& g = *GImGui;
6639 IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
6640 g.ItemFlagsStack.pop_back();
6641 g.CurrentItemFlags = g.ItemFlagsStack.back();
6642 }
6643
6644 // BeginDisabled()/EndDisabled()
6645 // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
6646 // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
6647 // - Feedback welcome at https://github.com/ocornut/imgui/issues/211
6648 // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
6649 // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()
BeginDisabled(bool disabled)6650 void ImGui::BeginDisabled(bool disabled)
6651 {
6652 ImGuiContext& g = *GImGui;
6653 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6654 if (!was_disabled && disabled)
6655 {
6656 g.DisabledAlphaBackup = g.Style.Alpha;
6657 g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
6658 }
6659 if (was_disabled || disabled)
6660 g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
6661 g.ItemFlagsStack.push_back(g.CurrentItemFlags);
6662 }
6663
EndDisabled()6664 void ImGui::EndDisabled()
6665 {
6666 ImGuiContext& g = *GImGui;
6667 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6668 //PopItemFlag();
6669 g.ItemFlagsStack.pop_back();
6670 g.CurrentItemFlags = g.ItemFlagsStack.back();
6671 if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
6672 g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
6673 }
6674
6675 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6676 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6677 {
6678 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6679 }
6680
PopAllowKeyboardFocus()6681 void ImGui::PopAllowKeyboardFocus()
6682 {
6683 PopItemFlag();
6684 }
6685
PushButtonRepeat(bool repeat)6686 void ImGui::PushButtonRepeat(bool repeat)
6687 {
6688 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6689 }
6690
PopButtonRepeat()6691 void ImGui::PopButtonRepeat()
6692 {
6693 PopItemFlag();
6694 }
6695
PushTextWrapPos(float wrap_pos_x)6696 void ImGui::PushTextWrapPos(float wrap_pos_x)
6697 {
6698 ImGuiWindow* window = GetCurrentWindow();
6699 window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
6700 window->DC.TextWrapPos = wrap_pos_x;
6701 }
6702
PopTextWrapPos()6703 void ImGui::PopTextWrapPos()
6704 {
6705 ImGuiWindow* window = GetCurrentWindow();
6706 window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
6707 window->DC.TextWrapPosStack.pop_back();
6708 }
6709
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6710 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6711 {
6712 if (window->RootWindow == potential_parent)
6713 return true;
6714 while (window != NULL)
6715 {
6716 if (window == potential_parent)
6717 return true;
6718 window = window->ParentWindow;
6719 }
6720 return false;
6721 }
6722
IsWindowAbove(ImGuiWindow * potential_above,ImGuiWindow * potential_below)6723 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
6724 {
6725 ImGuiContext& g = *GImGui;
6726 for (int i = g.Windows.Size - 1; i >= 0; i--)
6727 {
6728 ImGuiWindow* candidate_window = g.Windows[i];
6729 if (candidate_window == potential_above)
6730 return true;
6731 if (candidate_window == potential_below)
6732 return false;
6733 }
6734 return false;
6735 }
6736
IsWindowHovered(ImGuiHoveredFlags flags)6737 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6738 {
6739 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6740 ImGuiContext& g = *GImGui;
6741 if (g.HoveredWindow == NULL)
6742 return false;
6743
6744 if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
6745 {
6746 ImGuiWindow* window = g.CurrentWindow;
6747 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6748 {
6749 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6750 if (g.HoveredWindow->RootWindow != window->RootWindow)
6751 return false;
6752 break;
6753 case ImGuiHoveredFlags_RootWindow:
6754 if (g.HoveredWindow != window->RootWindow)
6755 return false;
6756 break;
6757 case ImGuiHoveredFlags_ChildWindows:
6758 if (!IsWindowChildOf(g.HoveredWindow, window))
6759 return false;
6760 break;
6761 default:
6762 if (g.HoveredWindow != window)
6763 return false;
6764 break;
6765 }
6766 }
6767
6768 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6769 return false;
6770 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6771 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6772 return false;
6773 return true;
6774 }
6775
IsWindowFocused(ImGuiFocusedFlags flags)6776 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6777 {
6778 ImGuiContext& g = *GImGui;
6779
6780 if (flags & ImGuiFocusedFlags_AnyWindow)
6781 return g.NavWindow != NULL;
6782
6783 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6784 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6785 {
6786 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6787 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6788 case ImGuiFocusedFlags_RootWindow:
6789 return g.NavWindow == g.CurrentWindow->RootWindow;
6790 case ImGuiFocusedFlags_ChildWindows:
6791 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6792 default:
6793 return g.NavWindow == g.CurrentWindow;
6794 }
6795 }
6796
6797 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6798 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
6799 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6800 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6801 {
6802 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6803 }
6804
GetWindowWidth()6805 float ImGui::GetWindowWidth()
6806 {
6807 ImGuiWindow* window = GImGui->CurrentWindow;
6808 return window->Size.x;
6809 }
6810
GetWindowHeight()6811 float ImGui::GetWindowHeight()
6812 {
6813 ImGuiWindow* window = GImGui->CurrentWindow;
6814 return window->Size.y;
6815 }
6816
GetWindowPos()6817 ImVec2 ImGui::GetWindowPos()
6818 {
6819 ImGuiContext& g = *GImGui;
6820 ImGuiWindow* window = g.CurrentWindow;
6821 return window->Pos;
6822 }
6823
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6824 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6825 {
6826 // Test condition (NB: bit 0 is always true) and clear flags for next time
6827 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6828 return;
6829
6830 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6831 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6832 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6833
6834 // Set
6835 const ImVec2 old_pos = window->Pos;
6836 window->Pos = ImFloor(pos);
6837 ImVec2 offset = window->Pos - old_pos;
6838 window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
6839 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6840 window->DC.IdealMaxPos += offset;
6841 window->DC.CursorStartPos += offset;
6842 }
6843
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6844 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6845 {
6846 ImGuiWindow* window = GetCurrentWindowRead();
6847 SetWindowPos(window, pos, cond);
6848 }
6849
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6850 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6851 {
6852 if (ImGuiWindow* window = FindWindowByName(name))
6853 SetWindowPos(window, pos, cond);
6854 }
6855
GetWindowSize()6856 ImVec2 ImGui::GetWindowSize()
6857 {
6858 ImGuiWindow* window = GetCurrentWindowRead();
6859 return window->Size;
6860 }
6861
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6862 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6863 {
6864 // Test condition (NB: bit 0 is always true) and clear flags for next time
6865 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6866 return;
6867
6868 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6869 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6870
6871 // Set
6872 if (size.x > 0.0f)
6873 {
6874 window->AutoFitFramesX = 0;
6875 window->SizeFull.x = IM_FLOOR(size.x);
6876 }
6877 else
6878 {
6879 window->AutoFitFramesX = 2;
6880 window->AutoFitOnlyGrows = false;
6881 }
6882 if (size.y > 0.0f)
6883 {
6884 window->AutoFitFramesY = 0;
6885 window->SizeFull.y = IM_FLOOR(size.y);
6886 }
6887 else
6888 {
6889 window->AutoFitFramesY = 2;
6890 window->AutoFitOnlyGrows = false;
6891 }
6892 }
6893
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6894 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6895 {
6896 SetWindowSize(GImGui->CurrentWindow, size, cond);
6897 }
6898
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6899 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6900 {
6901 if (ImGuiWindow* window = FindWindowByName(name))
6902 SetWindowSize(window, size, cond);
6903 }
6904
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6905 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6906 {
6907 // Test condition (NB: bit 0 is always true) and clear flags for next time
6908 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6909 return;
6910 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6911
6912 // Set
6913 window->Collapsed = collapsed;
6914 }
6915
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)6916 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
6917 {
6918 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
6919 window->HitTestHoleSize = ImVec2ih(size);
6920 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
6921 }
6922
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6923 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6924 {
6925 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6926 }
6927
IsWindowCollapsed()6928 bool ImGui::IsWindowCollapsed()
6929 {
6930 ImGuiWindow* window = GetCurrentWindowRead();
6931 return window->Collapsed;
6932 }
6933
IsWindowAppearing()6934 bool ImGui::IsWindowAppearing()
6935 {
6936 ImGuiWindow* window = GetCurrentWindowRead();
6937 return window->Appearing;
6938 }
6939
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6940 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6941 {
6942 if (ImGuiWindow* window = FindWindowByName(name))
6943 SetWindowCollapsed(window, collapsed, cond);
6944 }
6945
SetWindowFocus()6946 void ImGui::SetWindowFocus()
6947 {
6948 FocusWindow(GImGui->CurrentWindow);
6949 }
6950
SetWindowFocus(const char * name)6951 void ImGui::SetWindowFocus(const char* name)
6952 {
6953 if (name)
6954 {
6955 if (ImGuiWindow* window = FindWindowByName(name))
6956 FocusWindow(window);
6957 }
6958 else
6959 {
6960 FocusWindow(NULL);
6961 }
6962 }
6963
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6964 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6965 {
6966 ImGuiContext& g = *GImGui;
6967 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6968 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6969 g.NextWindowData.PosVal = pos;
6970 g.NextWindowData.PosPivotVal = pivot;
6971 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6972 }
6973
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6974 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6975 {
6976 ImGuiContext& g = *GImGui;
6977 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6978 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6979 g.NextWindowData.SizeVal = size;
6980 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6981 }
6982
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6983 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6984 {
6985 ImGuiContext& g = *GImGui;
6986 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6987 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6988 g.NextWindowData.SizeCallback = custom_callback;
6989 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6990 }
6991
6992 // Content size = inner scrollable rectangle, padded with WindowPadding.
6993 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6994 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6995 {
6996 ImGuiContext& g = *GImGui;
6997 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6998 g.NextWindowData.ContentSizeVal = ImFloor(size);
6999 }
7000
SetNextWindowScroll(const ImVec2 & scroll)7001 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
7002 {
7003 ImGuiContext& g = *GImGui;
7004 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
7005 g.NextWindowData.ScrollVal = scroll;
7006 }
7007
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)7008 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
7009 {
7010 ImGuiContext& g = *GImGui;
7011 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
7012 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
7013 g.NextWindowData.CollapsedVal = collapsed;
7014 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
7015 }
7016
SetNextWindowFocus()7017 void ImGui::SetNextWindowFocus()
7018 {
7019 ImGuiContext& g = *GImGui;
7020 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
7021 }
7022
SetNextWindowBgAlpha(float alpha)7023 void ImGui::SetNextWindowBgAlpha(float alpha)
7024 {
7025 ImGuiContext& g = *GImGui;
7026 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
7027 g.NextWindowData.BgAlphaVal = alpha;
7028 }
7029
GetWindowDrawList()7030 ImDrawList* ImGui::GetWindowDrawList()
7031 {
7032 ImGuiWindow* window = GetCurrentWindow();
7033 return window->DrawList;
7034 }
7035
GetFont()7036 ImFont* ImGui::GetFont()
7037 {
7038 return GImGui->Font;
7039 }
7040
GetFontSize()7041 float ImGui::GetFontSize()
7042 {
7043 return GImGui->FontSize;
7044 }
7045
GetFontTexUvWhitePixel()7046 ImVec2 ImGui::GetFontTexUvWhitePixel()
7047 {
7048 return GImGui->DrawListSharedData.TexUvWhitePixel;
7049 }
7050
SetWindowFontScale(float scale)7051 void ImGui::SetWindowFontScale(float scale)
7052 {
7053 IM_ASSERT(scale > 0.0f);
7054 ImGuiContext& g = *GImGui;
7055 ImGuiWindow* window = GetCurrentWindow();
7056 window->FontWindowScale = scale;
7057 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7058 }
7059
ActivateItem(ImGuiID id)7060 void ImGui::ActivateItem(ImGuiID id)
7061 {
7062 ImGuiContext& g = *GImGui;
7063 g.NavNextActivateId = id;
7064 }
7065
PushFocusScope(ImGuiID id)7066 void ImGui::PushFocusScope(ImGuiID id)
7067 {
7068 ImGuiContext& g = *GImGui;
7069 ImGuiWindow* window = g.CurrentWindow;
7070 g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
7071 window->DC.NavFocusScopeIdCurrent = id;
7072 }
7073
PopFocusScope()7074 void ImGui::PopFocusScope()
7075 {
7076 ImGuiContext& g = *GImGui;
7077 ImGuiWindow* window = g.CurrentWindow;
7078 IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
7079 window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
7080 g.FocusScopeStack.pop_back();
7081 }
7082
SetKeyboardFocusHere(int offset)7083 void ImGui::SetKeyboardFocusHere(int offset)
7084 {
7085 IM_ASSERT(offset >= -1); // -1 is allowed but not below
7086 ImGuiContext& g = *GImGui;
7087 ImGuiWindow* window = g.CurrentWindow;
7088 g.TabFocusRequestNextWindow = window;
7089 g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
7090 g.TabFocusRequestNextCounterTabStop = INT_MAX;
7091 }
7092
SetItemDefaultFocus()7093 void ImGui::SetItemDefaultFocus()
7094 {
7095 ImGuiContext& g = *GImGui;
7096 ImGuiWindow* window = g.CurrentWindow;
7097 if (!window->Appearing)
7098 return;
7099 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent)
7100 {
7101 g.NavInitRequest = false;
7102 g.NavInitResultId = g.LastItemData.ID;
7103 g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
7104 NavUpdateAnyRequestFlag();
7105 if (!IsItemVisible())
7106 SetScrollHereY();
7107 }
7108 }
7109
SetStateStorage(ImGuiStorage * tree)7110 void ImGui::SetStateStorage(ImGuiStorage* tree)
7111 {
7112 ImGuiWindow* window = GImGui->CurrentWindow;
7113 window->DC.StateStorage = tree ? tree : &window->StateStorage;
7114 }
7115
GetStateStorage()7116 ImGuiStorage* ImGui::GetStateStorage()
7117 {
7118 ImGuiWindow* window = GImGui->CurrentWindow;
7119 return window->DC.StateStorage;
7120 }
7121
PushID(const char * str_id)7122 void ImGui::PushID(const char* str_id)
7123 {
7124 ImGuiContext& g = *GImGui;
7125 ImGuiWindow* window = g.CurrentWindow;
7126 ImGuiID id = window->GetIDNoKeepAlive(str_id);
7127 window->IDStack.push_back(id);
7128 }
7129
PushID(const char * str_id_begin,const char * str_id_end)7130 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
7131 {
7132 ImGuiContext& g = *GImGui;
7133 ImGuiWindow* window = g.CurrentWindow;
7134 ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
7135 window->IDStack.push_back(id);
7136 }
7137
PushID(const void * ptr_id)7138 void ImGui::PushID(const void* ptr_id)
7139 {
7140 ImGuiContext& g = *GImGui;
7141 ImGuiWindow* window = g.CurrentWindow;
7142 ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
7143 window->IDStack.push_back(id);
7144 }
7145
PushID(int int_id)7146 void ImGui::PushID(int int_id)
7147 {
7148 ImGuiContext& g = *GImGui;
7149 ImGuiWindow* window = g.CurrentWindow;
7150 ImGuiID id = window->GetIDNoKeepAlive(int_id);
7151 window->IDStack.push_back(id);
7152 }
7153
7154 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)7155 void ImGui::PushOverrideID(ImGuiID id)
7156 {
7157 ImGuiContext& g = *GImGui;
7158 ImGuiWindow* window = g.CurrentWindow;
7159 window->IDStack.push_back(id);
7160 }
7161
7162 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
7163 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
7164 // for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
GetIDWithSeed(const char * str,const char * str_end,ImGuiID seed)7165 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
7166 {
7167 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
7168 ImGui::KeepAliveID(id);
7169 #ifdef IMGUI_ENABLE_TEST_ENGINE
7170 ImGuiContext& g = *GImGui;
7171 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
7172 #endif
7173 return id;
7174 }
7175
PopID()7176 void ImGui::PopID()
7177 {
7178 ImGuiWindow* window = GImGui->CurrentWindow;
7179 IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
7180 window->IDStack.pop_back();
7181 }
7182
GetID(const char * str_id)7183 ImGuiID ImGui::GetID(const char* str_id)
7184 {
7185 ImGuiWindow* window = GImGui->CurrentWindow;
7186 return window->GetID(str_id);
7187 }
7188
GetID(const char * str_id_begin,const char * str_id_end)7189 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
7190 {
7191 ImGuiWindow* window = GImGui->CurrentWindow;
7192 return window->GetID(str_id_begin, str_id_end);
7193 }
7194
GetID(const void * ptr_id)7195 ImGuiID ImGui::GetID(const void* ptr_id)
7196 {
7197 ImGuiWindow* window = GImGui->CurrentWindow;
7198 return window->GetID(ptr_id);
7199 }
7200
IsRectVisible(const ImVec2 & size)7201 bool ImGui::IsRectVisible(const ImVec2& size)
7202 {
7203 ImGuiWindow* window = GImGui->CurrentWindow;
7204 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7205 }
7206
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7207 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7208 {
7209 ImGuiWindow* window = GImGui->CurrentWindow;
7210 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7211 }
7212
7213
7214 //-----------------------------------------------------------------------------
7215 // [SECTION] ERROR CHECKING
7216 //-----------------------------------------------------------------------------
7217
7218 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
7219 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
7220 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
7221 // may see different structures than what imgui.cpp sees, which is problematic.
7222 // We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert,size_t sz_idx)7223 bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
7224 {
7225 bool error = false;
7226 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
7227 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
7228 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
7229 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
7230 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
7231 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
7232 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
7233 return !error;
7234 }
7235
ErrorCheckNewFrameSanityChecks()7236 static void ImGui::ErrorCheckNewFrameSanityChecks()
7237 {
7238 ImGuiContext& g = *GImGui;
7239
7240 // Check user IM_ASSERT macro
7241 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
7242 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
7243 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
7244 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
7245 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
7246 if (true) IM_ASSERT(1); else IM_ASSERT(0);
7247
7248 // Check user data
7249 // (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)
7250 IM_ASSERT(g.Initialized);
7251 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
7252 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
7253 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
7254 IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()");
7255 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
7256 IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
7257 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
7258 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
7259 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
7260 for (int n = 0; n < ImGuiKey_COUNT; n++)
7261 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)");
7262
7263 // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
7264 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
7265 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
7266
7267 // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
7268 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
7269 g.IO.ConfigWindowsResizeFromEdges = false;
7270 }
7271
ErrorCheckEndFrameSanityChecks()7272 static void ImGui::ErrorCheckEndFrameSanityChecks()
7273 {
7274 ImGuiContext& g = *GImGui;
7275
7276 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
7277 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
7278 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
7279 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
7280 // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
7281 // while still correctly asserting on mid-frame key press events.
7282 const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
7283 IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
7284 IM_UNUSED(key_mod_flags);
7285
7286 // Recover from errors
7287 //ErrorCheckEndFrameRecover();
7288
7289 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7290 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7291 if (g.CurrentWindowStack.Size != 1)
7292 {
7293 if (g.CurrentWindowStack.Size > 1)
7294 {
7295 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7296 while (g.CurrentWindowStack.Size > 1)
7297 End();
7298 }
7299 else
7300 {
7301 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7302 }
7303 }
7304
7305 IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7306 }
7307
7308 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7309 // Must be called during or before EndFrame().
7310 // This is generally flawed as we are not necessarily End/Popping things in the right order.
7311 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7312 // FIXME: Can't recover from interleaved BeginTabBar/Begin
ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback,void * user_data)7313 void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7314 {
7315 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7316 ImGuiContext& g = *GImGui;
7317 while (g.CurrentWindowStack.Size > 0)
7318 {
7319 while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7320 {
7321 if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7322 EndTable();
7323 }
7324 ImGuiWindow* window = g.CurrentWindow;
7325 IM_ASSERT(window != NULL);
7326 while (g.CurrentTabBar != NULL) //-V1044
7327 {
7328 if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7329 EndTabBar();
7330 }
7331 while (window->DC.TreeDepth > 0)
7332 {
7333 if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7334 TreePop();
7335 }
7336 while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7337 {
7338 if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7339 EndGroup();
7340 }
7341 while (window->IDStack.Size > 1)
7342 {
7343 if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7344 PopID();
7345 }
7346 while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7347 {
7348 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7349 PopStyleColor();
7350 }
7351 while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7352 {
7353 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7354 PopStyleVar();
7355 }
7356 while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7357 {
7358 if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7359 PopFocusScope();
7360 }
7361 if (g.CurrentWindowStack.Size == 1)
7362 {
7363 IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7364 break;
7365 }
7366 IM_ASSERT(window == g.CurrentWindow);
7367 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7368 {
7369 if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7370 EndChild();
7371 }
7372 else
7373 {
7374 if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7375 End();
7376 }
7377 }
7378 }
7379
7380 // Save current stack sizes for later compare
SetToCurrentState()7381 void ImGuiStackSizes::SetToCurrentState()
7382 {
7383 ImGuiContext& g = *GImGui;
7384 ImGuiWindow* window = g.CurrentWindow;
7385 SizeOfIDStack = (short)window->IDStack.Size;
7386 SizeOfColorStack = (short)g.ColorStack.Size;
7387 SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7388 SizeOfFontStack = (short)g.FontStack.Size;
7389 SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7390 SizeOfGroupStack = (short)g.GroupStack.Size;
7391 SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7392 }
7393
7394 // Compare to detect usage errors
CompareWithCurrentState()7395 void ImGuiStackSizes::CompareWithCurrentState()
7396 {
7397 ImGuiContext& g = *GImGui;
7398 ImGuiWindow* window = g.CurrentWindow;
7399 IM_UNUSED(window);
7400
7401 // Window stacks
7402 // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7403 IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!");
7404
7405 // Global stacks
7406 // 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.
7407 IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
7408 IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7409 IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
7410 IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
7411 IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
7412 IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!");
7413 }
7414
7415
7416 //-----------------------------------------------------------------------------
7417 // [SECTION] LAYOUT
7418 //-----------------------------------------------------------------------------
7419 // - ItemSize()
7420 // - ItemAdd()
7421 // - SameLine()
7422 // - GetCursorScreenPos()
7423 // - SetCursorScreenPos()
7424 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7425 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7426 // - GetCursorStartPos()
7427 // - Indent()
7428 // - Unindent()
7429 // - SetNextItemWidth()
7430 // - PushItemWidth()
7431 // - PushMultiItemsWidths()
7432 // - PopItemWidth()
7433 // - CalcItemWidth()
7434 // - CalcItemSize()
7435 // - GetTextLineHeight()
7436 // - GetTextLineHeightWithSpacing()
7437 // - GetFrameHeight()
7438 // - GetFrameHeightWithSpacing()
7439 // - GetContentRegionMax()
7440 // - GetContentRegionMaxAbs() [Internal]
7441 // - GetContentRegionAvail(),
7442 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7443 // - BeginGroup()
7444 // - EndGroup()
7445 // Also see in imgui_widgets: tab bars, columns.
7446 //-----------------------------------------------------------------------------
7447
7448 // Advance cursor given item size for layout.
7449 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7450 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
ItemSize(const ImVec2 & size,float text_baseline_y)7451 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7452 {
7453 ImGuiContext& g = *GImGui;
7454 ImGuiWindow* window = g.CurrentWindow;
7455 if (window->SkipItems)
7456 return;
7457
7458 // We increase the height in this function to accommodate for baseline offset.
7459 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7460 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7461 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7462 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7463
7464 // Always align ourselves on pixel boundaries
7465 //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]
7466 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7467 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7468 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
7469 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
7470 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7471 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7472 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7473
7474 window->DC.PrevLineSize.y = line_height;
7475 window->DC.CurrLineSize.y = 0.0f;
7476 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7477 window->DC.CurrLineTextBaseOffset = 0.0f;
7478
7479 // Horizontal layout mode
7480 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7481 SameLine();
7482 }
7483
ItemSize(const ImRect & bb,float text_baseline_y)7484 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7485 {
7486 ItemSize(bb.GetSize(), text_baseline_y);
7487 }
7488
7489 // Declare item bounding box for clipping and interaction.
7490 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7491 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg,ImGuiItemAddFlags flags)7492 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags)
7493 {
7494 ImGuiContext& g = *GImGui;
7495 ImGuiWindow* window = g.CurrentWindow;
7496
7497 // Set item data
7498 g.LastItemData.ID = id;
7499 g.LastItemData.Rect = bb;
7500 g.LastItemData.InFlags = g.CurrentItemFlags;
7501 g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
7502
7503 // Directional navigation processing
7504 if (id != 0)
7505 {
7506 // Runs prior to clipping early-out
7507 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7508 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7509 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7510 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7511 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7512 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7513 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7514 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7515 window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
7516 if (g.NavId == id || g.NavAnyRequest)
7517 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7518 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7519 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7520
7521 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7522 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7523 if (id == g.DebugItemPickerBreakId)
7524 {
7525 IM_DEBUG_BREAK();
7526 g.DebugItemPickerBreakId = 0;
7527 }
7528 #endif
7529 }
7530 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7531
7532 #ifdef IMGUI_ENABLE_TEST_ENGINE
7533 if (id != 0)
7534 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7535 #endif
7536
7537 // Clipping test
7538 const bool is_clipped = IsClippedEx(bb, id, false);
7539 if (is_clipped)
7540 return false;
7541 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7542
7543 // Tab stop handling (previously was using internal ItemFocusable() api)
7544 // FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343)
7545 if (flags & ImGuiItemAddFlags_Focusable)
7546 ItemFocusable(window, id);
7547
7548 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7549 if (IsMouseHoveringRect(bb.Min, bb.Max))
7550 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7551 return true;
7552 }
7553
7554 // Gets back to previous line and continue with horizontal layout
7555 // offset_from_start_x == 0 : follow right after previous item
7556 // offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7557 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
7558 // spacing_w >= 0 : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7559 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7560 {
7561 ImGuiWindow* window = GetCurrentWindow();
7562 if (window->SkipItems)
7563 return;
7564
7565 ImGuiContext& g = *GImGui;
7566 if (offset_from_start_x != 0.0f)
7567 {
7568 if (spacing_w < 0.0f) spacing_w = 0.0f;
7569 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7570 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7571 }
7572 else
7573 {
7574 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7575 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7576 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7577 }
7578 window->DC.CurrLineSize = window->DC.PrevLineSize;
7579 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7580 }
7581
GetCursorScreenPos()7582 ImVec2 ImGui::GetCursorScreenPos()
7583 {
7584 ImGuiWindow* window = GetCurrentWindowRead();
7585 return window->DC.CursorPos;
7586 }
7587
SetCursorScreenPos(const ImVec2 & pos)7588 void ImGui::SetCursorScreenPos(const ImVec2& pos)
7589 {
7590 ImGuiWindow* window = GetCurrentWindow();
7591 window->DC.CursorPos = pos;
7592 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7593 }
7594
7595 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7596 // 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'.
GetCursorPos()7597 ImVec2 ImGui::GetCursorPos()
7598 {
7599 ImGuiWindow* window = GetCurrentWindowRead();
7600 return window->DC.CursorPos - window->Pos + window->Scroll;
7601 }
7602
GetCursorPosX()7603 float ImGui::GetCursorPosX()
7604 {
7605 ImGuiWindow* window = GetCurrentWindowRead();
7606 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7607 }
7608
GetCursorPosY()7609 float ImGui::GetCursorPosY()
7610 {
7611 ImGuiWindow* window = GetCurrentWindowRead();
7612 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7613 }
7614
SetCursorPos(const ImVec2 & local_pos)7615 void ImGui::SetCursorPos(const ImVec2& local_pos)
7616 {
7617 ImGuiWindow* window = GetCurrentWindow();
7618 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7619 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7620 }
7621
SetCursorPosX(float x)7622 void ImGui::SetCursorPosX(float x)
7623 {
7624 ImGuiWindow* window = GetCurrentWindow();
7625 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7626 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7627 }
7628
SetCursorPosY(float y)7629 void ImGui::SetCursorPosY(float y)
7630 {
7631 ImGuiWindow* window = GetCurrentWindow();
7632 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7633 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7634 }
7635
GetCursorStartPos()7636 ImVec2 ImGui::GetCursorStartPos()
7637 {
7638 ImGuiWindow* window = GetCurrentWindowRead();
7639 return window->DC.CursorStartPos - window->Pos;
7640 }
7641
Indent(float indent_w)7642 void ImGui::Indent(float indent_w)
7643 {
7644 ImGuiContext& g = *GImGui;
7645 ImGuiWindow* window = GetCurrentWindow();
7646 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7647 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7648 }
7649
Unindent(float indent_w)7650 void ImGui::Unindent(float indent_w)
7651 {
7652 ImGuiContext& g = *GImGui;
7653 ImGuiWindow* window = GetCurrentWindow();
7654 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7655 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7656 }
7657
7658 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)7659 void ImGui::SetNextItemWidth(float item_width)
7660 {
7661 ImGuiContext& g = *GImGui;
7662 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7663 g.NextItemData.Width = item_width;
7664 }
7665
7666 // FIXME: Remove the == 0.0f behavior?
PushItemWidth(float item_width)7667 void ImGui::PushItemWidth(float item_width)
7668 {
7669 ImGuiContext& g = *GImGui;
7670 ImGuiWindow* window = g.CurrentWindow;
7671 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
7672 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7673 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7674 }
7675
PushMultiItemsWidths(int components,float w_full)7676 void ImGui::PushMultiItemsWidths(int components, float w_full)
7677 {
7678 ImGuiContext& g = *GImGui;
7679 ImGuiWindow* window = g.CurrentWindow;
7680 const ImGuiStyle& style = g.Style;
7681 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7682 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7683 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
7684 window->DC.ItemWidthStack.push_back(w_item_last);
7685 for (int i = 0; i < components - 2; i++)
7686 window->DC.ItemWidthStack.push_back(w_item_one);
7687 window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one;
7688 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7689 }
7690
PopItemWidth()7691 void ImGui::PopItemWidth()
7692 {
7693 ImGuiWindow* window = GetCurrentWindow();
7694 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7695 window->DC.ItemWidthStack.pop_back();
7696 }
7697
7698 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7699 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()7700 float ImGui::CalcItemWidth()
7701 {
7702 ImGuiContext& g = *GImGui;
7703 ImGuiWindow* window = g.CurrentWindow;
7704 float w;
7705 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7706 w = g.NextItemData.Width;
7707 else
7708 w = window->DC.ItemWidth;
7709 if (w < 0.0f)
7710 {
7711 float region_max_x = GetContentRegionMaxAbs().x;
7712 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7713 }
7714 w = IM_FLOOR(w);
7715 return w;
7716 }
7717
7718 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7719 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7720 // Note that only CalcItemWidth() is publicly exposed.
7721 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
CalcItemSize(ImVec2 size,float default_w,float default_h)7722 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7723 {
7724 ImGuiWindow* window = GImGui->CurrentWindow;
7725
7726 ImVec2 region_max;
7727 if (size.x < 0.0f || size.y < 0.0f)
7728 region_max = GetContentRegionMaxAbs();
7729
7730 if (size.x == 0.0f)
7731 size.x = default_w;
7732 else if (size.x < 0.0f)
7733 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7734
7735 if (size.y == 0.0f)
7736 size.y = default_h;
7737 else if (size.y < 0.0f)
7738 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7739
7740 return size;
7741 }
7742
GetTextLineHeight()7743 float ImGui::GetTextLineHeight()
7744 {
7745 ImGuiContext& g = *GImGui;
7746 return g.FontSize;
7747 }
7748
GetTextLineHeightWithSpacing()7749 float ImGui::GetTextLineHeightWithSpacing()
7750 {
7751 ImGuiContext& g = *GImGui;
7752 return g.FontSize + g.Style.ItemSpacing.y;
7753 }
7754
GetFrameHeight()7755 float ImGui::GetFrameHeight()
7756 {
7757 ImGuiContext& g = *GImGui;
7758 return g.FontSize + g.Style.FramePadding.y * 2.0f;
7759 }
7760
GetFrameHeightWithSpacing()7761 float ImGui::GetFrameHeightWithSpacing()
7762 {
7763 ImGuiContext& g = *GImGui;
7764 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7765 }
7766
7767 // FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience!
7768
7769 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()7770 ImVec2 ImGui::GetContentRegionMax()
7771 {
7772 ImGuiContext& g = *GImGui;
7773 ImGuiWindow* window = g.CurrentWindow;
7774 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7775 if (window->DC.CurrentColumns || g.CurrentTable)
7776 mx.x = window->WorkRect.Max.x - window->Pos.x;
7777 return mx;
7778 }
7779
7780 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()7781 ImVec2 ImGui::GetContentRegionMaxAbs()
7782 {
7783 ImGuiContext& g = *GImGui;
7784 ImGuiWindow* window = g.CurrentWindow;
7785 ImVec2 mx = window->ContentRegionRect.Max;
7786 if (window->DC.CurrentColumns || g.CurrentTable)
7787 mx.x = window->WorkRect.Max.x;
7788 return mx;
7789 }
7790
GetContentRegionAvail()7791 ImVec2 ImGui::GetContentRegionAvail()
7792 {
7793 ImGuiWindow* window = GImGui->CurrentWindow;
7794 return GetContentRegionMaxAbs() - window->DC.CursorPos;
7795 }
7796
7797 // In window space (not screen space!)
GetWindowContentRegionMin()7798 ImVec2 ImGui::GetWindowContentRegionMin()
7799 {
7800 ImGuiWindow* window = GImGui->CurrentWindow;
7801 return window->ContentRegionRect.Min - window->Pos;
7802 }
7803
GetWindowContentRegionMax()7804 ImVec2 ImGui::GetWindowContentRegionMax()
7805 {
7806 ImGuiWindow* window = GImGui->CurrentWindow;
7807 return window->ContentRegionRect.Max - window->Pos;
7808 }
7809
7810 // 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.)
7811 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()7812 void ImGui::BeginGroup()
7813 {
7814 ImGuiContext& g = *GImGui;
7815 ImGuiWindow* window = g.CurrentWindow;
7816
7817 g.GroupStack.resize(g.GroupStack.Size + 1);
7818 ImGuiGroupData& group_data = g.GroupStack.back();
7819 group_data.WindowID = window->ID;
7820 group_data.BackupCursorPos = window->DC.CursorPos;
7821 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7822 group_data.BackupIndent = window->DC.Indent;
7823 group_data.BackupGroupOffset = window->DC.GroupOffset;
7824 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7825 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7826 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7827 group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
7828 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7829 group_data.EmitItem = true;
7830
7831 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7832 window->DC.Indent = window->DC.GroupOffset;
7833 window->DC.CursorMaxPos = window->DC.CursorPos;
7834 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7835 if (g.LogEnabled)
7836 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
7837 }
7838
EndGroup()7839 void ImGui::EndGroup()
7840 {
7841 ImGuiContext& g = *GImGui;
7842 ImGuiWindow* window = g.CurrentWindow;
7843 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
7844
7845 ImGuiGroupData& group_data = g.GroupStack.back();
7846 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
7847
7848 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7849
7850 window->DC.CursorPos = group_data.BackupCursorPos;
7851 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7852 window->DC.Indent = group_data.BackupIndent;
7853 window->DC.GroupOffset = group_data.BackupGroupOffset;
7854 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7855 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7856 if (g.LogEnabled)
7857 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
7858
7859 if (!group_data.EmitItem)
7860 {
7861 g.GroupStack.pop_back();
7862 return;
7863 }
7864
7865 window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
7866 ItemSize(group_bb.GetSize());
7867 ItemAdd(group_bb, 0);
7868
7869 // 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.
7870 // 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.
7871 // Also if you grep for LastItemId you'll notice it is only used in that context.
7872 // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
7873 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7874 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
7875 if (group_contains_curr_active_id)
7876 g.LastItemData.ID = g.ActiveId;
7877 else if (group_contains_prev_active_id)
7878 g.LastItemData.ID = g.ActiveIdPreviousFrame;
7879 g.LastItemData.Rect = group_bb;
7880
7881 // Forward Hovered flag
7882 const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
7883 if (group_contains_curr_hovered_id)
7884 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
7885
7886 // Forward Edited flag
7887 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7888 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
7889
7890 // Forward Deactivated flag
7891 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7892 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7893 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
7894
7895 g.GroupStack.pop_back();
7896 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
7897 }
7898
7899
7900 //-----------------------------------------------------------------------------
7901 // [SECTION] SCROLLING
7902 //-----------------------------------------------------------------------------
7903
7904 // Helper to snap on edges when aiming at an item very close to the edge,
7905 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
7906 // When we refactor the scrolling API this may be configurable with a flag?
7907 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
CalcScrollEdgeSnap(float target,float snap_min,float snap_max,float snap_threshold,float center_ratio)7908 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
7909 {
7910 if (target <= snap_min + snap_threshold)
7911 return ImLerp(snap_min, target, center_ratio);
7912 if (target >= snap_max - snap_threshold)
7913 return ImLerp(target, snap_max, center_ratio);
7914 return target;
7915 }
7916
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)7917 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
7918 {
7919 ImVec2 scroll = window->Scroll;
7920 if (window->ScrollTarget.x < FLT_MAX)
7921 {
7922 float decoration_total_width = window->ScrollbarSizes.x;
7923 float center_x_ratio = window->ScrollTargetCenterRatio.x;
7924 float scroll_target_x = window->ScrollTarget.x;
7925 if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
7926 {
7927 float snap_x_min = 0.0f;
7928 float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width;
7929 scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
7930 }
7931 scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width);
7932 }
7933 if (window->ScrollTarget.y < FLT_MAX)
7934 {
7935 float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y;
7936 float center_y_ratio = window->ScrollTargetCenterRatio.y;
7937 float scroll_target_y = window->ScrollTarget.y;
7938 if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
7939 {
7940 float snap_y_min = 0.0f;
7941 float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height;
7942 scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
7943 }
7944 scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height);
7945 }
7946 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
7947 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
7948 if (!window->Collapsed && !window->SkipItems)
7949 {
7950 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7951 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7952 }
7953 return scroll;
7954 }
7955
7956 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7957 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7958 {
7959 ImGuiContext& g = *GImGui;
7960 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7961 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7962
7963 ImVec2 delta_scroll;
7964 if (!window_rect.Contains(item_rect))
7965 {
7966 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7967 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
7968 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7969 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7970 if (item_rect.Min.y < window_rect.Min.y)
7971 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7972 else if (item_rect.Max.y >= window_rect.Max.y)
7973 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7974
7975 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7976 delta_scroll = next_scroll - window->Scroll;
7977 }
7978
7979 // Also scroll parent window to keep us into view if necessary
7980 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7981 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7982
7983 return delta_scroll;
7984 }
7985
GetScrollX()7986 float ImGui::GetScrollX()
7987 {
7988 ImGuiWindow* window = GImGui->CurrentWindow;
7989 return window->Scroll.x;
7990 }
7991
GetScrollY()7992 float ImGui::GetScrollY()
7993 {
7994 ImGuiWindow* window = GImGui->CurrentWindow;
7995 return window->Scroll.y;
7996 }
7997
GetScrollMaxX()7998 float ImGui::GetScrollMaxX()
7999 {
8000 ImGuiWindow* window = GImGui->CurrentWindow;
8001 return window->ScrollMax.x;
8002 }
8003
GetScrollMaxY()8004 float ImGui::GetScrollMaxY()
8005 {
8006 ImGuiWindow* window = GImGui->CurrentWindow;
8007 return window->ScrollMax.y;
8008 }
8009
SetScrollX(ImGuiWindow * window,float scroll_x)8010 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
8011 {
8012 window->ScrollTarget.x = scroll_x;
8013 window->ScrollTargetCenterRatio.x = 0.0f;
8014 window->ScrollTargetEdgeSnapDist.x = 0.0f;
8015 }
8016
SetScrollY(ImGuiWindow * window,float scroll_y)8017 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
8018 {
8019 window->ScrollTarget.y = scroll_y;
8020 window->ScrollTargetCenterRatio.y = 0.0f;
8021 window->ScrollTargetEdgeSnapDist.y = 0.0f;
8022 }
8023
SetScrollX(float scroll_x)8024 void ImGui::SetScrollX(float scroll_x)
8025 {
8026 ImGuiContext& g = *GImGui;
8027 SetScrollX(g.CurrentWindow, scroll_x);
8028 }
8029
SetScrollY(float scroll_y)8030 void ImGui::SetScrollY(float scroll_y)
8031 {
8032 ImGuiContext& g = *GImGui;
8033 SetScrollY(g.CurrentWindow, scroll_y);
8034 }
8035
8036 // Note that a local position will vary depending on initial scroll value,
8037 // This is a little bit confusing so bear with us:
8038 // - local_pos = (absolution_pos - window->Pos)
8039 // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
8040 // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
8041 // - They mostly exists because of legacy API.
8042 // Following the rules above, when trying to work with scrolling code, consider that:
8043 // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
8044 // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
8045 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)8046 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
8047 {
8048 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
8049 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
8050 window->ScrollTargetCenterRatio.x = center_x_ratio;
8051 window->ScrollTargetEdgeSnapDist.x = 0.0f;
8052 }
8053
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)8054 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
8055 {
8056 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
8057 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect;
8058 local_y -= decoration_up_height;
8059 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
8060 window->ScrollTargetCenterRatio.y = center_y_ratio;
8061 window->ScrollTargetEdgeSnapDist.y = 0.0f;
8062 }
8063
SetScrollFromPosX(float local_x,float center_x_ratio)8064 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
8065 {
8066 ImGuiContext& g = *GImGui;
8067 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
8068 }
8069
SetScrollFromPosY(float local_y,float center_y_ratio)8070 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
8071 {
8072 ImGuiContext& g = *GImGui;
8073 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
8074 }
8075
8076 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
SetScrollHereX(float center_x_ratio)8077 void ImGui::SetScrollHereX(float center_x_ratio)
8078 {
8079 ImGuiContext& g = *GImGui;
8080 ImGuiWindow* window = g.CurrentWindow;
8081 float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
8082 float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
8083 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
8084
8085 // Tweak: snap on edges when aiming at an item very close to the edge
8086 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
8087 }
8088
8089 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHereY(float center_y_ratio)8090 void ImGui::SetScrollHereY(float center_y_ratio)
8091 {
8092 ImGuiContext& g = *GImGui;
8093 ImGuiWindow* window = g.CurrentWindow;
8094 float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
8095 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
8096 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
8097
8098 // Tweak: snap on edges when aiming at an item very close to the edge
8099 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
8100 }
8101
8102 //-----------------------------------------------------------------------------
8103 // [SECTION] TOOLTIPS
8104 //-----------------------------------------------------------------------------
8105
BeginTooltip()8106 void ImGui::BeginTooltip()
8107 {
8108 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
8109 }
8110
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)8111 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
8112 {
8113 ImGuiContext& g = *GImGui;
8114
8115 if (g.DragDropWithinSource || g.DragDropWithinTarget)
8116 {
8117 // 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)
8118 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
8119 // 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.
8120 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
8121 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
8122 SetNextWindowPos(tooltip_pos);
8123 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
8124 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
8125 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
8126 }
8127
8128 char window_name[16];
8129 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
8130 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
8131 if (ImGuiWindow* window = FindWindowByName(window_name))
8132 if (window->Active)
8133 {
8134 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
8135 window->Hidden = true;
8136 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
8137 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
8138 }
8139 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
8140 Begin(window_name, NULL, flags | extra_flags);
8141 }
8142
EndTooltip()8143 void ImGui::EndTooltip()
8144 {
8145 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
8146 End();
8147 }
8148
SetTooltipV(const char * fmt,va_list args)8149 void ImGui::SetTooltipV(const char* fmt, va_list args)
8150 {
8151 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
8152 TextV(fmt, args);
8153 EndTooltip();
8154 }
8155
SetTooltip(const char * fmt,...)8156 void ImGui::SetTooltip(const char* fmt, ...)
8157 {
8158 va_list args;
8159 va_start(args, fmt);
8160 SetTooltipV(fmt, args);
8161 va_end(args);
8162 }
8163
8164 //-----------------------------------------------------------------------------
8165 // [SECTION] POPUPS
8166 //-----------------------------------------------------------------------------
8167
8168 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)8169 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
8170 {
8171 ImGuiContext& g = *GImGui;
8172 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
8173 {
8174 // Return true if any popup is open at the current BeginPopup() level of the popup stack
8175 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
8176 IM_ASSERT(id == 0);
8177 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8178 return g.OpenPopupStack.Size > 0;
8179 else
8180 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
8181 }
8182 else
8183 {
8184 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8185 {
8186 // Return true if the popup is open anywhere in the popup stack
8187 for (int n = 0; n < g.OpenPopupStack.Size; n++)
8188 if (g.OpenPopupStack[n].PopupId == id)
8189 return true;
8190 return false;
8191 }
8192 else
8193 {
8194 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
8195 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
8196 }
8197 }
8198 }
8199
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)8200 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
8201 {
8202 ImGuiContext& g = *GImGui;
8203 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
8204 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
8205 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
8206 return IsPopupOpen(id, popup_flags);
8207 }
8208
GetTopMostPopupModal()8209 ImGuiWindow* ImGui::GetTopMostPopupModal()
8210 {
8211 ImGuiContext& g = *GImGui;
8212 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
8213 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
8214 if (popup->Flags & ImGuiWindowFlags_Modal)
8215 return popup;
8216 return NULL;
8217 }
8218
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)8219 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
8220 {
8221 ImGuiContext& g = *GImGui;
8222 OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
8223 }
8224
OpenPopup(ImGuiID id,ImGuiPopupFlags popup_flags)8225 void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
8226 {
8227 OpenPopupEx(id, popup_flags);
8228 }
8229
8230 // Mark popup as open (toggle toward open state).
8231 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
8232 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
8233 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags)8234 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
8235 {
8236 ImGuiContext& g = *GImGui;
8237 ImGuiWindow* parent_window = g.CurrentWindow;
8238 const int current_stack_size = g.BeginPopupStack.Size;
8239
8240 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
8241 if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
8242 return;
8243
8244 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
8245 popup_ref.PopupId = id;
8246 popup_ref.Window = NULL;
8247 popup_ref.SourceWindow = g.NavWindow;
8248 popup_ref.OpenFrameCount = g.FrameCount;
8249 popup_ref.OpenParentId = parent_window->IDStack.back();
8250 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
8251 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
8252
8253 IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
8254 if (g.OpenPopupStack.Size < current_stack_size + 1)
8255 {
8256 g.OpenPopupStack.push_back(popup_ref);
8257 }
8258 else
8259 {
8260 // 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
8261 // 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
8262 // 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.
8263 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
8264 {
8265 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
8266 }
8267 else
8268 {
8269 // Close child popups if any, then flag popup for open/reopen
8270 ClosePopupToLevel(current_stack_size, false);
8271 g.OpenPopupStack.push_back(popup_ref);
8272 }
8273
8274 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
8275 // This is equivalent to what ClosePopupToLevel() does.
8276 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
8277 // FocusWindow(parent_window);
8278 }
8279 }
8280
8281 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
8282 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)8283 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
8284 {
8285 ImGuiContext& g = *GImGui;
8286 if (g.OpenPopupStack.Size == 0)
8287 return;
8288
8289 // Don't close our own child popup windows.
8290 int popup_count_to_keep = 0;
8291 if (ref_window)
8292 {
8293 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
8294 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
8295 {
8296 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
8297 if (!popup.Window)
8298 continue;
8299 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
8300 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
8301 continue;
8302
8303 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
8304 // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8305 // Window -> Popup1 -> Popup2 -> Popup3
8306 // - Each popups may contain child windows, which is why we compare ->RootWindow!
8307 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8308 bool ref_window_is_descendent_of_popup = false;
8309 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8310 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8311 if (popup_window->RootWindow == ref_window->RootWindow)
8312 {
8313 ref_window_is_descendent_of_popup = true;
8314 break;
8315 }
8316 if (!ref_window_is_descendent_of_popup)
8317 break;
8318 }
8319 }
8320 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
8321 {
8322 IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8323 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8324 }
8325 }
8326
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)8327 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8328 {
8329 ImGuiContext& g = *GImGui;
8330 IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8331 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8332
8333 // Trim open popup stack
8334 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8335 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8336 g.OpenPopupStack.resize(remaining);
8337
8338 if (restore_focus_to_window_under_popup)
8339 {
8340 if (focus_window && !focus_window->WasActive && popup_window)
8341 {
8342 // Fallback
8343 FocusTopMostWindowUnderOne(popup_window, NULL);
8344 }
8345 else
8346 {
8347 if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8348 focus_window = NavRestoreLastChildNavWindow(focus_window);
8349 FocusWindow(focus_window);
8350 }
8351 }
8352 }
8353
8354 // Close the popup we have begin-ed into.
CloseCurrentPopup()8355 void ImGui::CloseCurrentPopup()
8356 {
8357 ImGuiContext& g = *GImGui;
8358 int popup_idx = g.BeginPopupStack.Size - 1;
8359 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8360 return;
8361
8362 // Closing a menu closes its top-most parent popup (unless a modal)
8363 while (popup_idx > 0)
8364 {
8365 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8366 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8367 bool close_parent = false;
8368 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8369 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8370 close_parent = true;
8371 if (!close_parent)
8372 break;
8373 popup_idx--;
8374 }
8375 IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8376 ClosePopupToLevel(popup_idx, true);
8377
8378 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8379 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8380 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8381 if (ImGuiWindow* window = g.NavWindow)
8382 window->DC.NavHideHighlightOneFrame = true;
8383 }
8384
8385 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)8386 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8387 {
8388 ImGuiContext& g = *GImGui;
8389 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8390 {
8391 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8392 return false;
8393 }
8394
8395 char name[20];
8396 if (flags & ImGuiWindowFlags_ChildMenu)
8397 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8398 else
8399 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8400
8401 flags |= ImGuiWindowFlags_Popup;
8402 bool is_open = Begin(name, NULL, flags);
8403 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8404 EndPopup();
8405
8406 return is_open;
8407 }
8408
BeginPopup(const char * str_id,ImGuiWindowFlags flags)8409 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8410 {
8411 ImGuiContext& g = *GImGui;
8412 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8413 {
8414 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8415 return false;
8416 }
8417 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8418 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8419 }
8420
8421 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8422 // Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here.
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)8423 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8424 {
8425 ImGuiContext& g = *GImGui;
8426 ImGuiWindow* window = g.CurrentWindow;
8427 const ImGuiID id = window->GetID(name);
8428 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8429 {
8430 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8431 return false;
8432 }
8433
8434 // Center modal windows by default for increased visibility
8435 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8436 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8437 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8438 {
8439 const ImGuiViewport* viewport = GetMainViewport();
8440 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8441 }
8442
8443 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
8444 const bool is_open = Begin(name, p_open, flags);
8445 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8446 {
8447 EndPopup();
8448 if (is_open)
8449 ClosePopupToLevel(g.BeginPopupStack.Size, true);
8450 return false;
8451 }
8452 return is_open;
8453 }
8454
EndPopup()8455 void ImGui::EndPopup()
8456 {
8457 ImGuiContext& g = *GImGui;
8458 ImGuiWindow* window = g.CurrentWindow;
8459 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
8460 IM_ASSERT(g.BeginPopupStack.Size > 0);
8461
8462 // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
8463 if (g.NavWindow == window)
8464 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8465
8466 // Child-popups don't need to be laid out
8467 IM_ASSERT(g.WithinEndChild == false);
8468 if (window->Flags & ImGuiWindowFlags_ChildWindow)
8469 g.WithinEndChild = true;
8470 End();
8471 g.WithinEndChild = false;
8472 }
8473
8474 // Helper to open a popup if mouse button is released over the item
8475 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
OpenPopupOnItemClick(const char * str_id,ImGuiPopupFlags popup_flags)8476 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
8477 {
8478 ImGuiContext& g = *GImGui;
8479 ImGuiWindow* window = g.CurrentWindow;
8480 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8481 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8482 {
8483 ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
8484 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8485 OpenPopupEx(id, popup_flags);
8486 }
8487 }
8488
8489 // This is a helper to handle the simplest case of associating one named popup to one given widget.
8490 // - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
8491 // - To create a popup with a specific identifier, pass it in str_id.
8492 // - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
8493 // - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
8494 // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8495 // This is essentially the same as:
8496 // id = str_id ? GetID(str_id) : GetItemID();
8497 // OpenPopupOnItemClick(str_id);
8498 // return BeginPopup(id);
8499 // Which is essentially the same as:
8500 // id = str_id ? GetID(str_id) : GetItemID();
8501 // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
8502 // OpenPopup(id);
8503 // return BeginPopup(id);
8504 // The main difference being that this is tweaked to avoid computing the ID twice.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)8505 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8506 {
8507 ImGuiContext& g = *GImGui;
8508 ImGuiWindow* window = g.CurrentWindow;
8509 if (window->SkipItems)
8510 return false;
8511 ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
8512 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8513 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8514 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8515 OpenPopupEx(id, popup_flags);
8516 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8517 }
8518
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)8519 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8520 {
8521 ImGuiContext& g = *GImGui;
8522 ImGuiWindow* window = g.CurrentWindow;
8523 if (!str_id)
8524 str_id = "window_context";
8525 ImGuiID id = window->GetID(str_id);
8526 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8527 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8528 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8529 OpenPopupEx(id, popup_flags);
8530 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8531 }
8532
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)8533 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8534 {
8535 ImGuiContext& g = *GImGui;
8536 ImGuiWindow* window = g.CurrentWindow;
8537 if (!str_id)
8538 str_id = "void_context";
8539 ImGuiID id = window->GetID(str_id);
8540 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8541 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8542 if (GetTopMostPopupModal() == NULL)
8543 OpenPopupEx(id, popup_flags);
8544 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8545 }
8546
8547 // 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.)
8548 // 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.
8549 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
8550 // information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
8551 // this allows us to have tooltips/popups displayed out of the parent viewport.)
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)8552 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8553 {
8554 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8555 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8556 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8557
8558 // Combo Box policy (we want a connecting edge)
8559 if (policy == ImGuiPopupPositionPolicy_ComboBox)
8560 {
8561 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8562 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8563 {
8564 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8565 if (n != -1 && dir == *last_dir) // Already tried this direction?
8566 continue;
8567 ImVec2 pos;
8568 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
8569 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8570 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8571 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8572 if (!r_outer.Contains(ImRect(pos, pos + size)))
8573 continue;
8574 *last_dir = dir;
8575 return pos;
8576 }
8577 }
8578
8579 // Tooltip and Default popup policy
8580 // (Always first try the direction we used on the last frame, if any)
8581 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
8582 {
8583 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8584 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8585 {
8586 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8587 if (n != -1 && dir == *last_dir) // Already tried this direction?
8588 continue;
8589
8590 const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
8591 const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
8592
8593 // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
8594 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
8595 continue;
8596 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
8597 continue;
8598
8599 ImVec2 pos;
8600 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8601 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
8602
8603 // Clamp top-left corner of popup
8604 pos.x = ImMax(pos.x, r_outer.Min.x);
8605 pos.y = ImMax(pos.y, r_outer.Min.y);
8606
8607 *last_dir = dir;
8608 return pos;
8609 }
8610 }
8611
8612 // Fallback when not enough room:
8613 *last_dir = ImGuiDir_None;
8614
8615 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8616 if (policy == ImGuiPopupPositionPolicy_Tooltip)
8617 return ref_pos + ImVec2(2, 2);
8618
8619 // Otherwise try to keep within display
8620 ImVec2 pos = ref_pos;
8621 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8622 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8623 return pos;
8624 }
8625
8626 // Note that this is used for popups, which can overlap the non work-area of individual viewports.
GetPopupAllowedExtentRect(ImGuiWindow * window)8627 ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
8628 {
8629 ImGuiContext& g = *GImGui;
8630 IM_UNUSED(window);
8631 ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
8632 ImVec2 padding = g.Style.DisplaySafeAreaPadding;
8633 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8634 return r_screen;
8635 }
8636
FindBestWindowPosForPopup(ImGuiWindow * window)8637 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8638 {
8639 ImGuiContext& g = *GImGui;
8640
8641 ImRect r_outer = GetPopupAllowedExtentRect(window);
8642 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8643 {
8644 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8645 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8646 IM_ASSERT(g.CurrentWindow == window);
8647 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
8648 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).
8649 ImRect r_avoid;
8650 if (parent_window->DC.MenuBarAppending)
8651 r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
8652 else
8653 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);
8654 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8655 }
8656 if (window->Flags & ImGuiWindowFlags_Popup)
8657 {
8658 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8659 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8660 }
8661 if (window->Flags & ImGuiWindowFlags_Tooltip)
8662 {
8663 // Position tooltip (always follows mouse)
8664 float sc = g.Style.MouseCursorScale;
8665 ImVec2 ref_pos = NavCalcPreferredRefPos();
8666 ImRect r_avoid;
8667 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8668 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8669 else
8670 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.
8671 return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
8672 }
8673 IM_ASSERT(0);
8674 return window->Pos;
8675 }
8676
8677 //-----------------------------------------------------------------------------
8678 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8679 //-----------------------------------------------------------------------------
8680
8681 // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked.
SetNavID(ImGuiID id,ImGuiNavLayer nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)8682 void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8683 {
8684 ImGuiContext& g = *GImGui;
8685 IM_ASSERT(g.NavWindow != NULL);
8686 IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
8687 g.NavId = id;
8688 g.NavLayer = nav_layer;
8689 g.NavFocusScopeId = focus_scope_id;
8690 g.NavWindow->NavLastIds[nav_layer] = id;
8691 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8692 //g.NavDisableHighlight = false;
8693 //g.NavDisableMouseHover = g.NavMousePosDirty = true;
8694 }
8695
SetFocusID(ImGuiID id,ImGuiWindow * window)8696 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8697 {
8698 ImGuiContext& g = *GImGui;
8699 IM_ASSERT(id != 0);
8700
8701 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8702 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8703 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8704 if (g.NavWindow != window)
8705 g.NavInitRequest = false;
8706 g.NavWindow = window;
8707 g.NavId = id;
8708 g.NavLayer = nav_layer;
8709 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8710 window->NavLastIds[nav_layer] = id;
8711 if (g.LastItemData.ID == id)
8712 window->NavRectRel[nav_layer] = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
8713
8714 if (g.ActiveIdSource == ImGuiInputSource_Nav)
8715 g.NavDisableMouseHover = true;
8716 else
8717 g.NavDisableHighlight = true;
8718 }
8719
ImGetDirQuadrantFromDelta(float dx,float dy)8720 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8721 {
8722 if (ImFabs(dx) > ImFabs(dy))
8723 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8724 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8725 }
8726
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)8727 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8728 {
8729 if (a1 < b0)
8730 return a1 - b0;
8731 if (b1 < a0)
8732 return a0 - b1;
8733 return 0.0f;
8734 }
8735
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)8736 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8737 {
8738 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8739 {
8740 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8741 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8742 }
8743 else
8744 {
8745 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8746 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8747 }
8748 }
8749
8750 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavItemData * result,ImRect cand)8751 static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
8752 {
8753 ImGuiContext& g = *GImGui;
8754 ImGuiWindow* window = g.CurrentWindow;
8755 if (g.NavLayer != window->DC.NavLayerCurrent)
8756 return false;
8757
8758 const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
8759 g.NavScoringCount++;
8760
8761 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8762 if (window->ParentWindow == g.NavWindow)
8763 {
8764 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8765 if (!window->ClipRect.Overlaps(cand))
8766 return false;
8767 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8768 }
8769
8770 // 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)
8771 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8772 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8773
8774 // Compute distance between boxes
8775 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8776 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8777 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
8778 if (dby != 0.0f && dbx != 0.0f)
8779 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8780 float dist_box = ImFabs(dbx) + ImFabs(dby);
8781
8782 // 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)
8783 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8784 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8785 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8786
8787 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8788 ImGuiDir quadrant;
8789 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8790 if (dbx != 0.0f || dby != 0.0f)
8791 {
8792 // For non-overlapping boxes, use distance between boxes
8793 dax = dbx;
8794 day = dby;
8795 dist_axial = dist_box;
8796 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8797 }
8798 else if (dcx != 0.0f || dcy != 0.0f)
8799 {
8800 // For overlapping boxes with different centers, use distance between centers
8801 dax = dcx;
8802 day = dcy;
8803 dist_axial = dist_center;
8804 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8805 }
8806 else
8807 {
8808 // 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)
8809 quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8810 }
8811
8812 #if IMGUI_DEBUG_NAV_SCORING
8813 char buf[128];
8814 if (IsMouseHoveringRect(cand.Min, cand.Max))
8815 {
8816 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]);
8817 ImDrawList* draw_list = GetForegroundDrawList(window);
8818 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8819 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8820 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8821 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8822 }
8823 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8824 {
8825 if (quadrant == g.NavMoveDir)
8826 {
8827 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8828 ImDrawList* draw_list = GetForegroundDrawList(window);
8829 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8830 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8831 }
8832 }
8833 #endif
8834
8835 // Is it in the quadrant we're interesting in moving to?
8836 bool new_best = false;
8837 const ImGuiDir move_dir = g.NavMoveDir;
8838 if (quadrant == move_dir)
8839 {
8840 // Does it beat the current best candidate?
8841 if (dist_box < result->DistBox)
8842 {
8843 result->DistBox = dist_box;
8844 result->DistCenter = dist_center;
8845 return true;
8846 }
8847 if (dist_box == result->DistBox)
8848 {
8849 // Try using distance between center points to break ties
8850 if (dist_center < result->DistCenter)
8851 {
8852 result->DistCenter = dist_center;
8853 new_best = true;
8854 }
8855 else if (dist_center == result->DistCenter)
8856 {
8857 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8858 // (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),
8859 // 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.
8860 if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8861 new_best = true;
8862 }
8863 }
8864 }
8865
8866 // 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
8867 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8868 // 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.
8869 // 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.
8870 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8871 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
8872 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8873 if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
8874 {
8875 result->DistAxial = dist_axial;
8876 new_best = true;
8877 }
8878
8879 return new_best;
8880 }
8881
NavApplyItemToResult(ImGuiNavItemData * result,ImGuiWindow * window,ImGuiID id,const ImRect & nav_bb_rel)8882 static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
8883 {
8884 result->Window = window;
8885 result->ID = id;
8886 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8887 result->RectRel = nav_bb_rel;
8888 }
8889
8890 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
NavProcessItem(ImGuiWindow * window,const ImRect & nav_bb,const ImGuiID id)8891 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8892 {
8893 ImGuiContext& g = *GImGui;
8894 const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
8895 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8896
8897 // Process Init Request
8898 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8899 {
8900 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8901 const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0;
8902 if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
8903 {
8904 g.NavInitResultId = id;
8905 g.NavInitResultRectRel = nav_bb_rel;
8906 }
8907 if (candidate_for_nav_default_focus)
8908 {
8909 g.NavInitRequest = false; // Found a match, clear request
8910 NavUpdateAnyRequestFlag();
8911 }
8912 }
8913
8914 // Process Move Request (scoring for navigation)
8915 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
8916 if (g.NavMoveScoringItems)
8917 {
8918 if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
8919 {
8920 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8921 bool new_best = NavScoreItem(result, nav_bb);
8922
8923 #if IMGUI_DEBUG_NAV_SCORING
8924 // [DEBUG] Scoring all items in NavWindow at all times
8925 if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult)
8926 new_best = false;
8927 #endif
8928 if (new_best)
8929 NavApplyItemToResult(result, window, id, nav_bb_rel);
8930
8931 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8932 const float VISIBLE_RATIO = 0.70f;
8933 if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8934 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)
8935 if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb))
8936 NavApplyItemToResult(&g.NavMoveResultLocalVisible, window, id, nav_bb_rel);
8937 }
8938 }
8939
8940 // Update window-relative bounding box of navigated item
8941 if (g.NavId == id)
8942 {
8943 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8944 g.NavLayer = window->DC.NavLayerCurrent;
8945 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8946 g.NavIdIsAlive = true;
8947 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
8948 }
8949 }
8950
NavMoveRequestButNoResultYet()8951 bool ImGui::NavMoveRequestButNoResultYet()
8952 {
8953 ImGuiContext& g = *GImGui;
8954 return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8955 }
8956
NavMoveRequestCancel()8957 void ImGui::NavMoveRequestCancel()
8958 {
8959 ImGuiContext& g = *GImGui;
8960 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
8961 NavUpdateAnyRequestFlag();
8962 }
8963
8964 // Forward will reuse the move request again on the next frame (generally with modifications done to it)
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,ImGuiNavMoveFlags move_flags)8965 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags)
8966 {
8967 ImGuiContext& g = *GImGui;
8968 IM_ASSERT(g.NavMoveForwardToNextFrame == false);
8969 NavMoveRequestCancel();
8970 g.NavMoveForwardToNextFrame = true;
8971 g.NavMoveDir = move_dir;
8972 g.NavMoveClipDir = clip_dir;
8973 g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
8974 }
8975
8976 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
8977 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags wrap_flags)8978 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
8979 {
8980 ImGuiContext& g = *GImGui;
8981 IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
8982 // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test
8983 if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
8984 g.NavMoveFlags |= wrap_flags;
8985 }
8986
8987 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8988 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8989 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8990 {
8991 ImGuiWindow* parent = nav_window;
8992 while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8993 parent = parent->ParentWindow;
8994 if (parent && parent != nav_window)
8995 parent->NavLastChildNavWindow = nav_window;
8996 }
8997
8998 // Restore the last focused child.
8999 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)9000 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
9001 {
9002 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
9003 return window->NavLastChildNavWindow;
9004 return window;
9005 }
9006
NavRestoreLayer(ImGuiNavLayer layer)9007 void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
9008 {
9009 ImGuiContext& g = *GImGui;
9010 if (layer == ImGuiNavLayer_Main)
9011 g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);
9012 ImGuiWindow* window = g.NavWindow;
9013 if (window->NavLastIds[layer] != 0)
9014 {
9015 SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
9016 g.NavDisableHighlight = false;
9017 g.NavDisableMouseHover = g.NavMousePosDirty = true;
9018 }
9019 else
9020 {
9021 g.NavLayer = layer;
9022 NavInitWindow(window, true);
9023 }
9024 }
9025
NavUpdateAnyRequestFlag()9026 static inline void ImGui::NavUpdateAnyRequestFlag()
9027 {
9028 ImGuiContext& g = *GImGui;
9029 g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
9030 if (g.NavAnyRequest)
9031 IM_ASSERT(g.NavWindow != NULL);
9032 }
9033
9034 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)9035 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
9036 {
9037 ImGuiContext& g = *GImGui;
9038 IM_ASSERT(window == g.NavWindow);
9039
9040 if (window->Flags & ImGuiWindowFlags_NoNavInputs)
9041 {
9042 g.NavId = g.NavFocusScopeId = 0;
9043 return;
9044 }
9045
9046 bool init_for_nav = false;
9047 if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
9048 init_for_nav = true;
9049 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
9050 if (init_for_nav)
9051 {
9052 SetNavID(0, g.NavLayer, 0, ImRect());
9053 g.NavInitRequest = true;
9054 g.NavInitRequestFromMove = false;
9055 g.NavInitResultId = 0;
9056 g.NavInitResultRectRel = ImRect();
9057 NavUpdateAnyRequestFlag();
9058 }
9059 else
9060 {
9061 g.NavId = window->NavLastIds[0];
9062 g.NavFocusScopeId = 0;
9063 }
9064 }
9065
NavCalcPreferredRefPos()9066 static ImVec2 ImGui::NavCalcPreferredRefPos()
9067 {
9068 ImGuiContext& g = *GImGui;
9069 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
9070 {
9071 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
9072 if (IsMousePosValid(&g.IO.MousePos))
9073 return g.IO.MousePos;
9074 return g.LastValidMousePos;
9075 }
9076 else
9077 {
9078 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
9079 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
9080 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()));
9081 ImGuiViewport* viewport = GetMainViewport();
9082 return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
9083 }
9084 }
9085
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)9086 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
9087 {
9088 ImGuiContext& g = *GImGui;
9089 if (mode == ImGuiInputReadMode_Down)
9090 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
9091
9092 const float t = g.IO.NavInputsDownDuration[n];
9093 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
9094 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
9095 if (t < 0.0f)
9096 return 0.0f;
9097 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
9098 return (t == 0.0f) ? 1.0f : 0.0f;
9099 if (mode == ImGuiInputReadMode_Repeat)
9100 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
9101 if (mode == ImGuiInputReadMode_RepeatSlow)
9102 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
9103 if (mode == ImGuiInputReadMode_RepeatFast)
9104 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
9105 return 0.0f;
9106 }
9107
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)9108 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
9109 {
9110 ImVec2 delta(0.0f, 0.0f);
9111 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
9112 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
9113 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
9114 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
9115 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
9116 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
9117 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
9118 delta *= slow_factor;
9119 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
9120 delta *= fast_factor;
9121 return delta;
9122 }
9123
NavUpdate()9124 static void ImGui::NavUpdate()
9125 {
9126 ImGuiContext& g = *GImGui;
9127 ImGuiIO& io = g.IO;
9128
9129 io.WantSetMousePos = false;
9130 #if 0
9131 if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
9132 #endif
9133
9134 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
9135 // (do it before we map Keyboard input!)
9136 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9137 const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9138 if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
9139 {
9140 if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f
9141 || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
9142 g.NavInputSource = ImGuiInputSource_Gamepad;
9143 }
9144
9145 // Update Keyboard->Nav inputs mapping
9146 if (nav_keyboard_active)
9147 {
9148 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0)
9149 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
9150 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
9151 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
9152 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
9153 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
9154 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
9155 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
9156 if (io.KeyCtrl)
9157 io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
9158 if (io.KeyShift)
9159 io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
9160 #undef NAV_MAP_KEY
9161 }
9162 memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
9163 for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
9164 io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
9165
9166 // Process navigation init request (select first/default focus)
9167 if (g.NavInitResultId != 0)
9168 NavUpdateInitResult();
9169 g.NavInitRequest = false;
9170 g.NavInitRequestFromMove = false;
9171 g.NavInitResultId = 0;
9172 g.NavJustMovedToId = 0;
9173
9174 // Process navigation move request
9175 if (g.NavMoveSubmitted)
9176 NavMoveRequestApplyResult();
9177 g.NavMoveSubmitted = g.NavMoveScoringItems = false;
9178
9179 // Apply application mouse position movement, after we had a chance to process move request result.
9180 if (g.NavMousePosDirty && g.NavIdIsAlive)
9181 {
9182 // Set mouse position given our knowledge of the navigated item position from last frame
9183 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
9184 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
9185 {
9186 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
9187 io.WantSetMousePos = true;
9188 }
9189 g.NavMousePosDirty = false;
9190 }
9191 g.NavIdIsAlive = false;
9192 g.NavJustTabbedId = 0;
9193 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
9194
9195 // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
9196 if (g.NavWindow)
9197 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
9198 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
9199 g.NavWindow->NavLastChildNavWindow = NULL;
9200
9201 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
9202 NavUpdateWindowing();
9203
9204 // Set output flags for user application
9205 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
9206 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
9207
9208 // Process NavCancel input (to close a popup, get back to parent, clear focus)
9209 NavUpdateCancelRequest();
9210
9211 // Process manual activation request
9212 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
9213 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9214 {
9215 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
9216 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
9217 if (g.ActiveId == 0 && activate_pressed)
9218 g.NavActivateId = g.NavId;
9219 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
9220 g.NavActivateDownId = g.NavId;
9221 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
9222 g.NavActivatePressedId = g.NavId;
9223 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
9224 g.NavInputId = g.NavId;
9225 }
9226 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9227 g.NavDisableHighlight = true;
9228 if (g.NavActivateId != 0)
9229 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
9230
9231 // Process programmatic activation request
9232 if (g.NavNextActivateId != 0)
9233 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
9234 g.NavNextActivateId = 0;
9235
9236 // Process move requests
9237 NavUpdateCreateMoveRequest();
9238 NavUpdateAnyRequestFlag();
9239
9240 // Scrolling
9241 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9242 {
9243 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9244 ImGuiWindow* window = g.NavWindow;
9245 const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
9246 const ImGuiDir move_dir = g.NavMoveDir;
9247 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None)
9248 {
9249 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
9250 SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9251 if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
9252 SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9253 }
9254
9255 // *Normal* Manual scroll with NavScrollXXX keys
9256 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9257 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9258 if (scroll_dir.x != 0.0f && window->ScrollbarX)
9259 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9260 if (scroll_dir.y != 0.0f)
9261 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9262 }
9263
9264 // [DEBUG]
9265 g.NavScoringCount = 0;
9266 #if IMGUI_DEBUG_NAV_RECTS
9267 if (g.NavWindow)
9268 {
9269 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9270 if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
9271 if (1) { 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); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
9272 }
9273 #endif
9274 }
9275
NavUpdateInitResult()9276 static void ImGui::NavUpdateInitResult()
9277 {
9278 // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
9279 ImGuiContext& g = *GImGui;
9280 if (!g.NavWindow)
9281 return;
9282
9283 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9284 // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
9285 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9286 SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9287 if (g.NavInitRequestFromMove)
9288 {
9289 g.NavDisableHighlight = false;
9290 g.NavDisableMouseHover = g.NavMousePosDirty = true;
9291 }
9292 }
9293
NavUpdateCreateMoveRequest()9294 void ImGui::NavUpdateCreateMoveRequest()
9295 {
9296 ImGuiContext& g = *GImGui;
9297 ImGuiIO& io = g.IO;
9298 ImGuiWindow* window = g.NavWindow;
9299
9300 if (g.NavMoveForwardToNextFrame && window != NULL)
9301 {
9302 // 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)
9303 // (preserve most state, which were already set by the NavMoveRequestForward() function)
9304 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
9305 IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
9306 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
9307 g.NavMoveForwardToNextFrame = false;
9308 }
9309 else
9310 {
9311 // Initiate directional inputs request
9312 g.NavMoveDir = ImGuiDir_None;
9313 g.NavMoveFlags = ImGuiNavMoveFlags_None;
9314 if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
9315 {
9316 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
9317 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
9318 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
9319 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
9320 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
9321 }
9322 g.NavMoveClipDir = g.NavMoveDir;
9323 }
9324
9325 // Update PageUp/PageDown/Home/End scroll
9326 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
9327 const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9328 float scoring_rect_offset_y = 0.0f;
9329 if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
9330 scoring_rect_offset_y = NavUpdatePageUpPageDown();
9331
9332 // [DEBUG] Always send a request
9333 #if IMGUI_DEBUG_NAV_SCORING
9334 if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
9335 g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
9336 if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None)
9337 {
9338 g.NavMoveDir = g.NavMoveDirForDebug;
9339 g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
9340 }
9341 #endif
9342
9343 // If we initiate a movement request and have no current NavId, we initiate a InitDefaultRequest that will be used as a fallback if the direction fails to find a match
9344 // FIXME: Would be nice to call a single function to initiate a new request
9345 if (g.NavMoveDir != ImGuiDir_None)
9346 {
9347 IM_ASSERT(window != NULL);
9348 g.NavMoveSubmitted = g.NavMoveScoringItems = true;
9349 g.NavMoveKeyMods = io.KeyMods;
9350 g.NavMoveDirForDebug = g.NavMoveDir;
9351 g.NavMoveResultLocal.Clear();
9352 g.NavMoveResultLocalVisible.Clear();
9353 g.NavMoveResultOther.Clear();
9354 }
9355
9356 // Moving with no reference triggers a init request
9357 if (g.NavMoveSubmitted && g.NavId == 0)
9358 {
9359 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
9360 g.NavInitRequest = g.NavInitRequestFromMove = true;
9361 g.NavInitResultId = 0;
9362 g.NavDisableHighlight = false;
9363 }
9364
9365 // When using gamepad, we project the reference nav bounding box into window visible area.
9366 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9367 // (can't focus a visible object like we can with the mouse).
9368 if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)
9369 {
9370 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9371 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9372 {
9373 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9374 float pad = window->CalcFontSize() * 0.5f;
9375 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
9376 window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9377 g.NavId = g.NavFocusScopeId = 0;
9378 }
9379 }
9380
9381 // 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)
9382 ImRect scoring_rect;
9383 if (window != NULL)
9384 {
9385 ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9386 scoring_rect = ImRect(window->Pos + nav_rect_rel.Min, window->Pos + nav_rect_rel.Max);
9387 scoring_rect.TranslateY(scoring_rect_offset_y);
9388 scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x);
9389 scoring_rect.Max.x = scoring_rect.Min.x;
9390 IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
9391 //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9392 }
9393 g.NavScoringRect = scoring_rect;
9394 }
9395
9396 // Apply result from previous frame navigation directional move request. Always called from NavUpdate()
NavMoveRequestApplyResult()9397 void ImGui::NavMoveRequestApplyResult()
9398 {
9399 ImGuiContext& g = *GImGui;
9400
9401 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9402 {
9403 // 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)
9404 if (g.NavId != 0)
9405 {
9406 g.NavDisableHighlight = false;
9407 g.NavDisableMouseHover = true;
9408 }
9409 return;
9410 }
9411
9412 // Select which result to use
9413 ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9414
9415 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9416 if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9417 if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
9418 result = &g.NavMoveResultLocalVisible;
9419
9420 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9421 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9422 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9423 result = &g.NavMoveResultOther;
9424 IM_ASSERT(g.NavWindow && result->Window);
9425
9426 // Scroll to keep newly navigated item fully into view.
9427 if (g.NavLayer == ImGuiNavLayer_Main)
9428 {
9429 ImVec2 delta_scroll;
9430 if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdge)
9431 {
9432 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9433 delta_scroll.y = result->Window->Scroll.y - scroll_target;
9434 SetScrollY(result->Window, scroll_target);
9435 }
9436 else
9437 {
9438 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9439 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9440 }
9441
9442 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9443 result->RectRel.TranslateX(-delta_scroll.x);
9444 result->RectRel.TranslateY(-delta_scroll.y);
9445 }
9446
9447 ClearActiveID();
9448 g.NavWindow = result->Window;
9449 if (g.NavId != result->ID)
9450 {
9451 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9452 g.NavJustMovedToId = result->ID;
9453 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9454 g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
9455 }
9456 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
9457 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9458 g.NavDisableHighlight = false;
9459 g.NavDisableMouseHover = g.NavMousePosDirty = true;
9460 }
9461
9462 // Process NavCancel input (to close a popup, get back to parent, clear focus)
9463 // FIXME: In order to support e.g. Escape to clear a selection we'll need:
9464 // - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
9465 // - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
NavUpdateCancelRequest()9466 static void ImGui::NavUpdateCancelRequest()
9467 {
9468 ImGuiContext& g = *GImGui;
9469 if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
9470 return;
9471
9472 IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
9473 if (g.ActiveId != 0)
9474 {
9475 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
9476 ClearActiveID();
9477 }
9478 else if (g.NavLayer != ImGuiNavLayer_Main)
9479 {
9480 // Leave the "menu" layer
9481 NavRestoreLayer(ImGuiNavLayer_Main);
9482 }
9483 else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
9484 {
9485 // Exit child window
9486 ImGuiWindow* child_window = g.NavWindow;
9487 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
9488 IM_ASSERT(child_window->ChildId != 0);
9489 ImRect child_rect = child_window->Rect();
9490 FocusWindow(parent_window);
9491 SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
9492 }
9493 else if (g.OpenPopupStack.Size > 0)
9494 {
9495 // Close open popup/menu
9496 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
9497 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
9498 }
9499 else
9500 {
9501 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
9502 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
9503 g.NavWindow->NavLastIds[0] = 0;
9504 g.NavId = g.NavFocusScopeId = 0;
9505 }
9506 }
9507
9508 // Handle PageUp/PageDown/Home/End keys
9509 // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
9510 // FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
NavUpdatePageUpPageDown()9511 static float ImGui::NavUpdatePageUpPageDown()
9512 {
9513 ImGuiContext& g = *GImGui;
9514 ImGuiIO& io = g.IO;
9515
9516 ImGuiWindow* window = g.NavWindow;
9517 if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9518 return 0.0f;
9519
9520 const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9521 const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9522 const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9523 const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9524 if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
9525 return 0.0f;
9526
9527 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
9528 {
9529 // Fallback manual-scroll when window has no navigable item
9530 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9531 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9532 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9533 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9534 else if (home_pressed)
9535 SetScrollY(window, 0.0f);
9536 else if (end_pressed)
9537 SetScrollY(window, window->ScrollMax.y);
9538 }
9539 else
9540 {
9541 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9542 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9543 float nav_scoring_rect_offset_y = 0.0f;
9544 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9545 {
9546 nav_scoring_rect_offset_y = -page_offset_y;
9547 g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
9548 g.NavMoveClipDir = ImGuiDir_Up;
9549 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9550 }
9551 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9552 {
9553 nav_scoring_rect_offset_y = +page_offset_y;
9554 g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
9555 g.NavMoveClipDir = ImGuiDir_Down;
9556 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9557 }
9558 else if (home_pressed)
9559 {
9560 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9561 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9562 // Preserve current horizontal position if we have any.
9563 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9564 if (nav_rect_rel.IsInverted())
9565 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9566 g.NavMoveDir = ImGuiDir_Down;
9567 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9568 // FIXME-NAV: MoveClipDir left to _None, intentional?
9569 }
9570 else if (end_pressed)
9571 {
9572 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9573 if (nav_rect_rel.IsInverted())
9574 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9575 g.NavMoveDir = ImGuiDir_Up;
9576 g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9577 // FIXME-NAV: MoveClipDir left to _None, intentional?
9578 }
9579 return nav_scoring_rect_offset_y;
9580 }
9581 return 0.0f;
9582 }
9583
NavEndFrame()9584 static void ImGui::NavEndFrame()
9585 {
9586 ImGuiContext& g = *GImGui;
9587
9588 // Show CTRL+TAB list window
9589 if (g.NavWindowingTarget != NULL)
9590 NavUpdateWindowingOverlay();
9591
9592 // Perform wrap-around in menus
9593 // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
9594 ImGuiWindow* window = g.NavWindow;
9595 const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
9596 const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY;
9597 if (window && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
9598 {
9599 bool do_forward = false;
9600 ImRect bb_rel = window->NavRectRel[g.NavLayer];
9601 ImGuiDir clip_dir = g.NavMoveDir;
9602 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9603 {
9604 bb_rel.Min.x = bb_rel.Max.x =
9605 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9606 if (move_flags & ImGuiNavMoveFlags_WrapX)
9607 {
9608 bb_rel.TranslateY(-bb_rel.GetHeight());
9609 clip_dir = ImGuiDir_Up;
9610 }
9611 do_forward = true;
9612 }
9613 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9614 {
9615 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9616 if (move_flags & ImGuiNavMoveFlags_WrapX)
9617 {
9618 bb_rel.TranslateY(+bb_rel.GetHeight());
9619 clip_dir = ImGuiDir_Down;
9620 }
9621 do_forward = true;
9622 }
9623 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9624 {
9625 bb_rel.Min.y = bb_rel.Max.y =
9626 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9627 if (move_flags & ImGuiNavMoveFlags_WrapY)
9628 {
9629 bb_rel.TranslateX(-bb_rel.GetWidth());
9630 clip_dir = ImGuiDir_Left;
9631 }
9632 do_forward = true;
9633 }
9634 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9635 {
9636 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9637 if (move_flags & ImGuiNavMoveFlags_WrapY)
9638 {
9639 bb_rel.TranslateX(+bb_rel.GetWidth());
9640 clip_dir = ImGuiDir_Right;
9641 }
9642 do_forward = true;
9643 }
9644 if (do_forward)
9645 {
9646 window->NavRectRel[g.NavLayer] = bb_rel;
9647 NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags);
9648 }
9649 }
9650 }
9651
FindWindowFocusIndex(ImGuiWindow * window)9652 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
9653 {
9654 ImGuiContext& g = *GImGui;
9655 IM_UNUSED(g);
9656 int order = window->FocusOrder;
9657 IM_ASSERT(g.WindowsFocusOrder[order] == window);
9658 return order;
9659 }
9660
FindWindowNavFocusable(int i_start,int i_stop,int dir)9661 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9662 {
9663 ImGuiContext& g = *GImGui;
9664 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9665 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9666 return g.WindowsFocusOrder[i];
9667 return NULL;
9668 }
9669
NavUpdateWindowingHighlightWindow(int focus_change_dir)9670 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9671 {
9672 ImGuiContext& g = *GImGui;
9673 IM_ASSERT(g.NavWindowingTarget);
9674 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9675 return;
9676
9677 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9678 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9679 if (!window_target)
9680 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9681 if (window_target) // Don't reset windowing target if there's a single window in the list
9682 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9683 g.NavWindowingToggleLayer = false;
9684 }
9685
9686 // Windowing management mode
9687 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9688 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()9689 static void ImGui::NavUpdateWindowing()
9690 {
9691 ImGuiContext& g = *GImGui;
9692 ImGuiIO& io = g.IO;
9693
9694 ImGuiWindow* apply_focus_window = NULL;
9695 bool apply_toggle_layer = false;
9696
9697 ImGuiWindow* modal_window = GetTopMostPopupModal();
9698 bool allow_windowing = (modal_window == NULL);
9699 if (!allow_windowing)
9700 g.NavWindowingTarget = NULL;
9701
9702 // Fade out
9703 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9704 {
9705 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
9706 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9707 g.NavWindowingTargetAnim = NULL;
9708 }
9709
9710 // Start CTRL-TAB or Square+L/R window selection
9711 bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9712 bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9713 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9714 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9715 {
9716 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
9717 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9718 g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer
9719 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
9720 }
9721
9722 // Gamepad update
9723 g.NavWindowingTimer += io.DeltaTime;
9724 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
9725 {
9726 // 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
9727 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9728
9729 // Select window to focus
9730 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9731 if (focus_change_dir != 0)
9732 {
9733 NavUpdateWindowingHighlightWindow(focus_change_dir);
9734 g.NavWindowingHighlightAlpha = 1.0f;
9735 }
9736
9737 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9738 if (!IsNavInputDown(ImGuiNavInput_Menu))
9739 {
9740 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9741 if (g.NavWindowingToggleLayer && g.NavWindow)
9742 apply_toggle_layer = true;
9743 else if (!g.NavWindowingToggleLayer)
9744 apply_focus_window = g.NavWindowingTarget;
9745 g.NavWindowingTarget = NULL;
9746 }
9747 }
9748
9749 // Keyboard: Focus
9750 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
9751 {
9752 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9753 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9754 if (IsKeyPressedMap(ImGuiKey_Tab, true))
9755 NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1);
9756 if (!io.KeyCtrl)
9757 apply_focus_window = g.NavWindowingTarget;
9758 }
9759
9760 // Keyboard: Press and Release ALT to toggle menu layer
9761 // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.
9762 // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
9763 if (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0)
9764 {
9765 g.NavWindowingToggleLayer = true;
9766 g.NavInputSource = ImGuiInputSource_Keyboard;
9767 }
9768 if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)
9769 {
9770 // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
9771 // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
9772 if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
9773 g.NavWindowingToggleLayer = false;
9774
9775 // Apply layer toggle on release
9776 // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false)
9777 // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
9778 if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer)
9779 if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
9780 if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
9781 apply_toggle_layer = true;
9782 if (!io.KeyAlt)
9783 g.NavWindowingToggleLayer = false;
9784 }
9785
9786 // Move window
9787 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9788 {
9789 ImVec2 move_delta;
9790 if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
9791 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9792 if (g.NavInputSource == ImGuiInputSource_Gamepad)
9793 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9794 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9795 {
9796 const float NAV_MOVE_SPEED = 800.0f;
9797 const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
9798 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9799 SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9800 MarkIniSettingsDirty(moving_window);
9801 g.NavDisableMouseHover = true;
9802 }
9803 }
9804
9805 // Apply final focus
9806 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
9807 {
9808 ClearActiveID();
9809 g.NavDisableHighlight = false;
9810 g.NavDisableMouseHover = true;
9811 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9812 ClosePopupsOverWindow(apply_focus_window, false);
9813 FocusWindow(apply_focus_window);
9814 if (apply_focus_window->NavLastIds[0] == 0)
9815 NavInitWindow(apply_focus_window, false);
9816
9817 // If the window has ONLY a menu layer (no main layer), select it directly
9818 // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
9819 // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
9820 // the target window as already been previewed once.
9821 // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
9822 // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
9823 // won't be valid.
9824 if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
9825 g.NavLayer = ImGuiNavLayer_Menu;
9826 }
9827 if (apply_focus_window)
9828 g.NavWindowingTarget = NULL;
9829
9830 // Apply menu/layer toggle
9831 if (apply_toggle_layer && g.NavWindow)
9832 {
9833 ClearActiveID();
9834
9835 // Move to parent menu if necessary
9836 ImGuiWindow* new_nav_window = g.NavWindow;
9837 while (new_nav_window->ParentWindow
9838 && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9839 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9840 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9841 new_nav_window = new_nav_window->ParentWindow;
9842 if (new_nav_window != g.NavWindow)
9843 {
9844 ImGuiWindow* old_nav_window = g.NavWindow;
9845 FocusWindow(new_nav_window);
9846 new_nav_window->NavLastChildNavWindow = old_nav_window;
9847 }
9848 g.NavDisableHighlight = false;
9849 g.NavDisableMouseHover = true;
9850
9851 // Reinitialize navigation when entering menu bar with the Alt key.
9852 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9853 if (new_nav_layer == ImGuiNavLayer_Menu)
9854 g.NavWindow->NavLastIds[new_nav_layer] = 0;
9855 NavRestoreLayer(new_nav_layer);
9856 }
9857 }
9858
9859 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)9860 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9861 {
9862 if (window->Flags & ImGuiWindowFlags_Popup)
9863 return "(Popup)";
9864 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9865 return "(Main menu bar)";
9866 return "(Untitled)";
9867 }
9868
9869 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()9870 void ImGui::NavUpdateWindowingOverlay()
9871 {
9872 ImGuiContext& g = *GImGui;
9873 IM_ASSERT(g.NavWindowingTarget != NULL);
9874
9875 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9876 return;
9877
9878 if (g.NavWindowingListWindow == NULL)
9879 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9880 const ImGuiViewport* viewport = GetMainViewport();
9881 SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9882 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9883 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9884 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9885 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9886 {
9887 ImGuiWindow* window = g.WindowsFocusOrder[n];
9888 IM_ASSERT(window != NULL); // Fix static analyzers
9889 if (!IsWindowNavFocusable(window))
9890 continue;
9891 const char* label = window->Name;
9892 if (label == FindRenderedTextEnd(label))
9893 label = GetFallbackWindowNameForWindowingList(window);
9894 Selectable(label, g.NavWindowingTarget == window);
9895 }
9896 End();
9897 PopStyleVar();
9898 }
9899
9900
9901 //-----------------------------------------------------------------------------
9902 // [SECTION] DRAG AND DROP
9903 //-----------------------------------------------------------------------------
9904
ClearDragDrop()9905 void ImGui::ClearDragDrop()
9906 {
9907 ImGuiContext& g = *GImGui;
9908 g.DragDropActive = false;
9909 g.DragDropPayload.Clear();
9910 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9911 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9912 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9913 g.DragDropAcceptFrameCount = -1;
9914
9915 g.DragDropPayloadBufHeap.clear();
9916 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9917 }
9918
9919 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
9920 // If the item has an identifier:
9921 // - This assume/require the item to be activated (typically via ButtonBehavior).
9922 // - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
9923 // - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
9924 // If the item has no identifier:
9925 // - Currently always assume left mouse button.
BeginDragDropSource(ImGuiDragDropFlags flags)9926 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9927 {
9928 ImGuiContext& g = *GImGui;
9929 ImGuiWindow* window = g.CurrentWindow;
9930
9931 // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
9932 // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
9933 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9934
9935 bool source_drag_active = false;
9936 ImGuiID source_id = 0;
9937 ImGuiID source_parent_id = 0;
9938 if (!(flags & ImGuiDragDropFlags_SourceExtern))
9939 {
9940 source_id = g.LastItemData.ID;
9941 if (source_id != 0)
9942 {
9943 // Common path: items with ID
9944 if (g.ActiveId != source_id)
9945 return false;
9946 if (g.ActiveIdMouseButton != -1)
9947 mouse_button = g.ActiveIdMouseButton;
9948 if (g.IO.MouseDown[mouse_button] == false)
9949 return false;
9950 g.ActiveIdAllowOverlap = false;
9951 }
9952 else
9953 {
9954 // Uncommon path: items without ID
9955 if (g.IO.MouseDown[mouse_button] == false)
9956 return false;
9957
9958 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
9959 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
9960 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
9961 {
9962 IM_ASSERT(0);
9963 return false;
9964 }
9965
9966 // Early out
9967 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
9968 return false;
9969
9970 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
9971 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
9972 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
9973 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
9974 // Rely on keeping other window->LastItemXXX fields intact.
9975 source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
9976 bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id);
9977 if (is_hovered && g.IO.MouseClicked[mouse_button])
9978 {
9979 SetActiveID(source_id, window);
9980 FocusWindow(window);
9981 }
9982 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
9983 g.ActiveIdAllowOverlap = is_hovered;
9984 }
9985 if (g.ActiveId != source_id)
9986 return false;
9987 source_parent_id = window->IDStack.back();
9988 source_drag_active = IsMouseDragging(mouse_button);
9989
9990 // Disable navigation and key inputs while dragging + cancel existing request if any
9991 SetActiveIdUsingNavAndKeys();
9992 }
9993 else
9994 {
9995 window = NULL;
9996 source_id = ImHashStr("#SourceExtern");
9997 source_drag_active = true;
9998 }
9999
10000 if (source_drag_active)
10001 {
10002 if (!g.DragDropActive)
10003 {
10004 IM_ASSERT(source_id != 0);
10005 ClearDragDrop();
10006 ImGuiPayload& payload = g.DragDropPayload;
10007 payload.SourceId = source_id;
10008 payload.SourceParentId = source_parent_id;
10009 g.DragDropActive = true;
10010 g.DragDropSourceFlags = flags;
10011 g.DragDropMouseButton = mouse_button;
10012 if (payload.SourceId == g.ActiveId)
10013 g.ActiveIdNoClearOnFocusLoss = true;
10014 }
10015 g.DragDropSourceFrameCount = g.FrameCount;
10016 g.DragDropWithinSource = true;
10017
10018 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10019 {
10020 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
10021 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
10022 BeginTooltip();
10023 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
10024 {
10025 ImGuiWindow* tooltip_window = g.CurrentWindow;
10026 tooltip_window->Hidden = tooltip_window->SkipItems = true;
10027 tooltip_window->HiddenFramesCanSkipItems = 1;
10028 }
10029 }
10030
10031 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
10032 g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
10033
10034 return true;
10035 }
10036 return false;
10037 }
10038
EndDragDropSource()10039 void ImGui::EndDragDropSource()
10040 {
10041 ImGuiContext& g = *GImGui;
10042 IM_ASSERT(g.DragDropActive);
10043 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
10044
10045 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
10046 EndTooltip();
10047
10048 // Discard the drag if have not called SetDragDropPayload()
10049 if (g.DragDropPayload.DataFrameCount == -1)
10050 ClearDragDrop();
10051 g.DragDropWithinSource = false;
10052 }
10053
10054 // Use 'cond' to choose to submit payload on drag start or every frame
SetDragDropPayload(const char * type,const void * data,size_t data_size,ImGuiCond cond)10055 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
10056 {
10057 ImGuiContext& g = *GImGui;
10058 ImGuiPayload& payload = g.DragDropPayload;
10059 if (cond == 0)
10060 cond = ImGuiCond_Always;
10061
10062 IM_ASSERT(type != NULL);
10063 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
10064 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
10065 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
10066 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
10067
10068 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
10069 {
10070 // Copy payload
10071 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
10072 g.DragDropPayloadBufHeap.resize(0);
10073 if (data_size > sizeof(g.DragDropPayloadBufLocal))
10074 {
10075 // Store in heap
10076 g.DragDropPayloadBufHeap.resize((int)data_size);
10077 payload.Data = g.DragDropPayloadBufHeap.Data;
10078 memcpy(payload.Data, data, data_size);
10079 }
10080 else if (data_size > 0)
10081 {
10082 // Store locally
10083 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
10084 payload.Data = g.DragDropPayloadBufLocal;
10085 memcpy(payload.Data, data, data_size);
10086 }
10087 else
10088 {
10089 payload.Data = NULL;
10090 }
10091 payload.DataSize = (int)data_size;
10092 }
10093 payload.DataFrameCount = g.FrameCount;
10094
10095 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
10096 }
10097
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)10098 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
10099 {
10100 ImGuiContext& g = *GImGui;
10101 if (!g.DragDropActive)
10102 return false;
10103
10104 ImGuiWindow* window = g.CurrentWindow;
10105 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10106 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10107 return false;
10108 IM_ASSERT(id != 0);
10109 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
10110 return false;
10111 if (window->SkipItems)
10112 return false;
10113
10114 IM_ASSERT(g.DragDropWithinTarget == false);
10115 g.DragDropTargetRect = bb;
10116 g.DragDropTargetId = id;
10117 g.DragDropWithinTarget = true;
10118 return true;
10119 }
10120
10121 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
10122 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
10123 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
10124 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()10125 bool ImGui::BeginDragDropTarget()
10126 {
10127 ImGuiContext& g = *GImGui;
10128 if (!g.DragDropActive)
10129 return false;
10130
10131 ImGuiWindow* window = g.CurrentWindow;
10132 if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
10133 return false;
10134 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10135 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10136 return false;
10137
10138 const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
10139 ImGuiID id = g.LastItemData.ID;
10140 if (id == 0)
10141 id = window->GetIDFromRectangle(display_rect);
10142 if (g.DragDropPayload.SourceId == id)
10143 return false;
10144
10145 IM_ASSERT(g.DragDropWithinTarget == false);
10146 g.DragDropTargetRect = display_rect;
10147 g.DragDropTargetId = id;
10148 g.DragDropWithinTarget = true;
10149 return true;
10150 }
10151
IsDragDropPayloadBeingAccepted()10152 bool ImGui::IsDragDropPayloadBeingAccepted()
10153 {
10154 ImGuiContext& g = *GImGui;
10155 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
10156 }
10157
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)10158 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
10159 {
10160 ImGuiContext& g = *GImGui;
10161 ImGuiWindow* window = g.CurrentWindow;
10162 ImGuiPayload& payload = g.DragDropPayload;
10163 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
10164 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
10165 if (type != NULL && !payload.IsDataType(type))
10166 return NULL;
10167
10168 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
10169 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
10170 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
10171 ImRect r = g.DragDropTargetRect;
10172 float r_surface = r.GetWidth() * r.GetHeight();
10173 if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
10174 {
10175 g.DragDropAcceptFlags = flags;
10176 g.DragDropAcceptIdCurr = g.DragDropTargetId;
10177 g.DragDropAcceptIdCurrRectSurface = r_surface;
10178 }
10179
10180 // Render default drop visuals
10181 // FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
10182 payload.Preview = was_accepted_previously;
10183 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
10184 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
10185 window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
10186
10187 g.DragDropAcceptFrameCount = g.FrameCount;
10188 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()
10189 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
10190 return NULL;
10191
10192 return &payload;
10193 }
10194
GetDragDropPayload()10195 const ImGuiPayload* ImGui::GetDragDropPayload()
10196 {
10197 ImGuiContext& g = *GImGui;
10198 return g.DragDropActive ? &g.DragDropPayload : NULL;
10199 }
10200
10201 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()10202 void ImGui::EndDragDropTarget()
10203 {
10204 ImGuiContext& g = *GImGui;
10205 IM_ASSERT(g.DragDropActive);
10206 IM_ASSERT(g.DragDropWithinTarget);
10207 g.DragDropWithinTarget = false;
10208 }
10209
10210 //-----------------------------------------------------------------------------
10211 // [SECTION] LOGGING/CAPTURING
10212 //-----------------------------------------------------------------------------
10213 // All text output from the interface can be captured into tty/file/clipboard.
10214 // By default, tree nodes are automatically opened during logging.
10215 //-----------------------------------------------------------------------------
10216
10217 // Pass text data straight to log (without being displayed)
LogTextV(ImGuiContext & g,const char * fmt,va_list args)10218 static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
10219 {
10220 if (g.LogFile)
10221 {
10222 g.LogBuffer.Buf.resize(0);
10223 g.LogBuffer.appendfv(fmt, args);
10224 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
10225 }
10226 else
10227 {
10228 g.LogBuffer.appendfv(fmt, args);
10229 }
10230 }
10231
LogText(const char * fmt,...)10232 void ImGui::LogText(const char* fmt, ...)
10233 {
10234 ImGuiContext& g = *GImGui;
10235 if (!g.LogEnabled)
10236 return;
10237
10238 va_list args;
10239 va_start(args, fmt);
10240 LogTextV(g, fmt, args);
10241 va_end(args);
10242 }
10243
LogTextV(const char * fmt,va_list args)10244 void ImGui::LogTextV(const char* fmt, va_list args)
10245 {
10246 ImGuiContext& g = *GImGui;
10247 if (!g.LogEnabled)
10248 return;
10249
10250 LogTextV(g, fmt, args);
10251 }
10252
10253 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
10254 // We split text into individual lines to add current tree level padding
10255 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)10256 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
10257 {
10258 ImGuiContext& g = *GImGui;
10259 ImGuiWindow* window = g.CurrentWindow;
10260
10261 const char* prefix = g.LogNextPrefix;
10262 const char* suffix = g.LogNextSuffix;
10263 g.LogNextPrefix = g.LogNextSuffix = NULL;
10264
10265 if (!text_end)
10266 text_end = FindRenderedTextEnd(text, text_end);
10267
10268 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
10269 if (ref_pos)
10270 g.LogLinePosY = ref_pos->y;
10271 if (log_new_line)
10272 {
10273 LogText(IM_NEWLINE);
10274 g.LogLineFirstItem = true;
10275 }
10276
10277 if (prefix)
10278 LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
10279
10280 // Re-adjust padding if we have popped out of our starting depth
10281 if (g.LogDepthRef > window->DC.TreeDepth)
10282 g.LogDepthRef = window->DC.TreeDepth;
10283 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
10284
10285 const char* text_remaining = text;
10286 for (;;)
10287 {
10288 // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
10289 // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
10290 const char* line_start = text_remaining;
10291 const char* line_end = ImStreolRange(line_start, text_end);
10292 const bool is_last_line = (line_end == text_end);
10293 if (line_start != line_end || !is_last_line)
10294 {
10295 const int line_length = (int)(line_end - line_start);
10296 const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
10297 LogText("%*s%.*s", indentation, "", line_length, line_start);
10298 g.LogLineFirstItem = false;
10299 if (*line_end == '\n')
10300 {
10301 LogText(IM_NEWLINE);
10302 g.LogLineFirstItem = true;
10303 }
10304 }
10305 if (is_last_line)
10306 break;
10307 text_remaining = line_end + 1;
10308 }
10309
10310 if (suffix)
10311 LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
10312 }
10313
10314 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)10315 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
10316 {
10317 ImGuiContext& g = *GImGui;
10318 ImGuiWindow* window = g.CurrentWindow;
10319 IM_ASSERT(g.LogEnabled == false);
10320 IM_ASSERT(g.LogFile == NULL);
10321 IM_ASSERT(g.LogBuffer.empty());
10322 g.LogEnabled = true;
10323 g.LogType = type;
10324 g.LogNextPrefix = g.LogNextSuffix = NULL;
10325 g.LogDepthRef = window->DC.TreeDepth;
10326 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
10327 g.LogLinePosY = FLT_MAX;
10328 g.LogLineFirstItem = true;
10329 }
10330
10331 // Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
LogSetNextTextDecoration(const char * prefix,const char * suffix)10332 void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
10333 {
10334 ImGuiContext& g = *GImGui;
10335 g.LogNextPrefix = prefix;
10336 g.LogNextSuffix = suffix;
10337 }
10338
LogToTTY(int auto_open_depth)10339 void ImGui::LogToTTY(int auto_open_depth)
10340 {
10341 ImGuiContext& g = *GImGui;
10342 if (g.LogEnabled)
10343 return;
10344 IM_UNUSED(auto_open_depth);
10345 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10346 LogBegin(ImGuiLogType_TTY, auto_open_depth);
10347 g.LogFile = stdout;
10348 #endif
10349 }
10350
10351 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)10352 void ImGui::LogToFile(int auto_open_depth, const char* filename)
10353 {
10354 ImGuiContext& g = *GImGui;
10355 if (g.LogEnabled)
10356 return;
10357
10358 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
10359 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
10360 // By opening the file in binary mode "ab" we have consistent output everywhere.
10361 if (!filename)
10362 filename = g.IO.LogFilename;
10363 if (!filename || !filename[0])
10364 return;
10365 ImFileHandle f = ImFileOpen(filename, "ab");
10366 if (!f)
10367 {
10368 IM_ASSERT(0);
10369 return;
10370 }
10371
10372 LogBegin(ImGuiLogType_File, auto_open_depth);
10373 g.LogFile = f;
10374 }
10375
10376 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)10377 void ImGui::LogToClipboard(int auto_open_depth)
10378 {
10379 ImGuiContext& g = *GImGui;
10380 if (g.LogEnabled)
10381 return;
10382 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
10383 }
10384
LogToBuffer(int auto_open_depth)10385 void ImGui::LogToBuffer(int auto_open_depth)
10386 {
10387 ImGuiContext& g = *GImGui;
10388 if (g.LogEnabled)
10389 return;
10390 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
10391 }
10392
LogFinish()10393 void ImGui::LogFinish()
10394 {
10395 ImGuiContext& g = *GImGui;
10396 if (!g.LogEnabled)
10397 return;
10398
10399 LogText(IM_NEWLINE);
10400 switch (g.LogType)
10401 {
10402 case ImGuiLogType_TTY:
10403 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10404 fflush(g.LogFile);
10405 #endif
10406 break;
10407 case ImGuiLogType_File:
10408 ImFileClose(g.LogFile);
10409 break;
10410 case ImGuiLogType_Buffer:
10411 break;
10412 case ImGuiLogType_Clipboard:
10413 if (!g.LogBuffer.empty())
10414 SetClipboardText(g.LogBuffer.begin());
10415 break;
10416 case ImGuiLogType_None:
10417 IM_ASSERT(0);
10418 break;
10419 }
10420
10421 g.LogEnabled = false;
10422 g.LogType = ImGuiLogType_None;
10423 g.LogFile = NULL;
10424 g.LogBuffer.clear();
10425 }
10426
10427 // Helper to display logging buttons
10428 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()10429 void ImGui::LogButtons()
10430 {
10431 ImGuiContext& g = *GImGui;
10432
10433 PushID("LogButtons");
10434 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10435 const bool log_to_tty = Button("Log To TTY"); SameLine();
10436 #else
10437 const bool log_to_tty = false;
10438 #endif
10439 const bool log_to_file = Button("Log To File"); SameLine();
10440 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10441 PushAllowKeyboardFocus(false);
10442 SetNextItemWidth(80.0f);
10443 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10444 PopAllowKeyboardFocus();
10445 PopID();
10446
10447 // Start logging at the end of the function so that the buttons don't appear in the log
10448 if (log_to_tty)
10449 LogToTTY();
10450 if (log_to_file)
10451 LogToFile();
10452 if (log_to_clipboard)
10453 LogToClipboard();
10454 }
10455
10456
10457 //-----------------------------------------------------------------------------
10458 // [SECTION] SETTINGS
10459 //-----------------------------------------------------------------------------
10460 // - UpdateSettings() [Internal]
10461 // - MarkIniSettingsDirty() [Internal]
10462 // - CreateNewWindowSettings() [Internal]
10463 // - FindWindowSettings() [Internal]
10464 // - FindOrCreateWindowSettings() [Internal]
10465 // - FindSettingsHandler() [Internal]
10466 // - ClearIniSettings() [Internal]
10467 // - LoadIniSettingsFromDisk()
10468 // - LoadIniSettingsFromMemory()
10469 // - SaveIniSettingsToDisk()
10470 // - SaveIniSettingsToMemory()
10471 // - WindowSettingsHandler_***() [Internal]
10472 //-----------------------------------------------------------------------------
10473
10474 // Called by NewFrame()
UpdateSettings()10475 void ImGui::UpdateSettings()
10476 {
10477 // Load settings on first frame (if not explicitly loaded manually before)
10478 ImGuiContext& g = *GImGui;
10479 if (!g.SettingsLoaded)
10480 {
10481 IM_ASSERT(g.SettingsWindows.empty());
10482 if (g.IO.IniFilename)
10483 LoadIniSettingsFromDisk(g.IO.IniFilename);
10484 g.SettingsLoaded = true;
10485 }
10486
10487 // Save settings (with a delay after the last modification, so we don't spam disk too much)
10488 if (g.SettingsDirtyTimer > 0.0f)
10489 {
10490 g.SettingsDirtyTimer -= g.IO.DeltaTime;
10491 if (g.SettingsDirtyTimer <= 0.0f)
10492 {
10493 if (g.IO.IniFilename != NULL)
10494 SaveIniSettingsToDisk(g.IO.IniFilename);
10495 else
10496 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10497 g.SettingsDirtyTimer = 0.0f;
10498 }
10499 }
10500 }
10501
MarkIniSettingsDirty()10502 void ImGui::MarkIniSettingsDirty()
10503 {
10504 ImGuiContext& g = *GImGui;
10505 if (g.SettingsDirtyTimer <= 0.0f)
10506 g.SettingsDirtyTimer = g.IO.IniSavingRate;
10507 }
10508
MarkIniSettingsDirty(ImGuiWindow * window)10509 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10510 {
10511 ImGuiContext& g = *GImGui;
10512 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10513 if (g.SettingsDirtyTimer <= 0.0f)
10514 g.SettingsDirtyTimer = g.IO.IniSavingRate;
10515 }
10516
CreateNewWindowSettings(const char * name)10517 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10518 {
10519 ImGuiContext& g = *GImGui;
10520
10521 #if !IMGUI_DEBUG_INI_SETTINGS
10522 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10523 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10524 if (const char* p = strstr(name, "###"))
10525 name = p;
10526 #endif
10527 const size_t name_len = strlen(name);
10528
10529 // Allocate chunk
10530 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10531 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10532 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10533 settings->ID = ImHashStr(name, name_len);
10534 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
10535
10536 return settings;
10537 }
10538
FindWindowSettings(ImGuiID id)10539 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10540 {
10541 ImGuiContext& g = *GImGui;
10542 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10543 if (settings->ID == id)
10544 return settings;
10545 return NULL;
10546 }
10547
FindOrCreateWindowSettings(const char * name)10548 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10549 {
10550 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10551 return settings;
10552 return CreateNewWindowSettings(name);
10553 }
10554
FindSettingsHandler(const char * type_name)10555 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10556 {
10557 ImGuiContext& g = *GImGui;
10558 const ImGuiID type_hash = ImHashStr(type_name);
10559 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10560 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10561 return &g.SettingsHandlers[handler_n];
10562 return NULL;
10563 }
10564
ClearIniSettings()10565 void ImGui::ClearIniSettings()
10566 {
10567 ImGuiContext& g = *GImGui;
10568 g.SettingsIniData.clear();
10569 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10570 if (g.SettingsHandlers[handler_n].ClearAllFn)
10571 g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10572 }
10573
LoadIniSettingsFromDisk(const char * ini_filename)10574 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10575 {
10576 size_t file_data_size = 0;
10577 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10578 if (!file_data)
10579 return;
10580 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10581 IM_FREE(file_data);
10582 }
10583
10584 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)10585 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10586 {
10587 ImGuiContext& g = *GImGui;
10588 IM_ASSERT(g.Initialized);
10589 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10590 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10591
10592 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10593 // 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..
10594 if (ini_size == 0)
10595 ini_size = strlen(ini_data);
10596 g.SettingsIniData.Buf.resize((int)ini_size + 1);
10597 char* const buf = g.SettingsIniData.Buf.Data;
10598 char* const buf_end = buf + ini_size;
10599 memcpy(buf, ini_data, ini_size);
10600 buf_end[0] = 0;
10601
10602 // Call pre-read handlers
10603 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10604 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10605 if (g.SettingsHandlers[handler_n].ReadInitFn)
10606 g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10607
10608 void* entry_data = NULL;
10609 ImGuiSettingsHandler* entry_handler = NULL;
10610
10611 char* line_end = NULL;
10612 for (char* line = buf; line < buf_end; line = line_end + 1)
10613 {
10614 // Skip new lines markers, then find end of the line
10615 while (*line == '\n' || *line == '\r')
10616 line++;
10617 line_end = line;
10618 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10619 line_end++;
10620 line_end[0] = 0;
10621 if (line[0] == ';')
10622 continue;
10623 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10624 {
10625 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10626 line_end[-1] = 0;
10627 const char* name_end = line_end - 1;
10628 const char* type_start = line + 1;
10629 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10630 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10631 if (!type_end || !name_start)
10632 continue;
10633 *type_end = 0; // Overwrite first ']'
10634 name_start++; // Skip second '['
10635 entry_handler = FindSettingsHandler(type_start);
10636 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10637 }
10638 else if (entry_handler != NULL && entry_data != NULL)
10639 {
10640 // Let type handler parse the line
10641 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10642 }
10643 }
10644 g.SettingsLoaded = true;
10645
10646 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10647 memcpy(buf, ini_data, ini_size);
10648
10649 // Call post-read handlers
10650 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10651 if (g.SettingsHandlers[handler_n].ApplyAllFn)
10652 g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10653 }
10654
SaveIniSettingsToDisk(const char * ini_filename)10655 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10656 {
10657 ImGuiContext& g = *GImGui;
10658 g.SettingsDirtyTimer = 0.0f;
10659 if (!ini_filename)
10660 return;
10661
10662 size_t ini_data_size = 0;
10663 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10664 ImFileHandle f = ImFileOpen(ini_filename, "wt");
10665 if (!f)
10666 return;
10667 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10668 ImFileClose(f);
10669 }
10670
10671 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)10672 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10673 {
10674 ImGuiContext& g = *GImGui;
10675 g.SettingsDirtyTimer = 0.0f;
10676 g.SettingsIniData.Buf.resize(0);
10677 g.SettingsIniData.Buf.push_back(0);
10678 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10679 {
10680 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10681 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10682 }
10683 if (out_size)
10684 *out_size = (size_t)g.SettingsIniData.size();
10685 return g.SettingsIniData.c_str();
10686 }
10687
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10688 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10689 {
10690 ImGuiContext& g = *ctx;
10691 for (int i = 0; i != g.Windows.Size; i++)
10692 g.Windows[i]->SettingsOffset = -1;
10693 g.SettingsWindows.clear();
10694 }
10695
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)10696 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
10697 {
10698 ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10699 ImGuiID id = settings->ID;
10700 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10701 settings->ID = id;
10702 settings->WantApply = true;
10703 return (void*)settings;
10704 }
10705
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)10706 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10707 {
10708 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10709 int x, y;
10710 int i;
10711 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
10712 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
10713 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
10714 }
10715
10716 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10717 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10718 {
10719 ImGuiContext& g = *ctx;
10720 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10721 if (settings->WantApply)
10722 {
10723 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10724 ApplyWindowSettings(window, settings);
10725 settings->WantApply = false;
10726 }
10727 }
10728
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)10729 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10730 {
10731 // Gather data from windows that were active during this session
10732 // (if a window wasn't opened in this session we preserve its settings)
10733 ImGuiContext& g = *ctx;
10734 for (int i = 0; i != g.Windows.Size; i++)
10735 {
10736 ImGuiWindow* window = g.Windows[i];
10737 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10738 continue;
10739
10740 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10741 if (!settings)
10742 {
10743 settings = ImGui::CreateNewWindowSettings(window->Name);
10744 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10745 }
10746 IM_ASSERT(settings->ID == window->ID);
10747 settings->Pos = ImVec2ih(window->Pos);
10748 settings->Size = ImVec2ih(window->SizeFull);
10749
10750 settings->Collapsed = window->Collapsed;
10751 }
10752
10753 // Write to text buffer
10754 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10755 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10756 {
10757 const char* settings_name = settings->GetName();
10758 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10759 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10760 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10761 buf->appendf("Collapsed=%d\n", settings->Collapsed);
10762 buf->append("\n");
10763 }
10764 }
10765
10766
10767 //-----------------------------------------------------------------------------
10768 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
10769 //-----------------------------------------------------------------------------
10770 // - GetMainViewport()
10771 // - UpdateViewportsNewFrame() [Internal]
10772 // (this section is more complete in the 'docking' branch)
10773 //-----------------------------------------------------------------------------
10774
GetMainViewport()10775 ImGuiViewport* ImGui::GetMainViewport()
10776 {
10777 ImGuiContext& g = *GImGui;
10778 return g.Viewports[0];
10779 }
10780
10781 // Update viewports and monitor infos
UpdateViewportsNewFrame()10782 static void ImGui::UpdateViewportsNewFrame()
10783 {
10784 ImGuiContext& g = *GImGui;
10785 IM_ASSERT(g.Viewports.Size == 1);
10786
10787 // Update main viewport with current platform position.
10788 // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
10789 ImGuiViewportP* main_viewport = g.Viewports[0];
10790 main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
10791 main_viewport->Pos = ImVec2(0.0f, 0.0f);
10792 main_viewport->Size = g.IO.DisplaySize;
10793
10794 for (int n = 0; n < g.Viewports.Size; n++)
10795 {
10796 ImGuiViewportP* viewport = g.Viewports[n];
10797
10798 // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
10799 viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
10800 viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
10801 viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);
10802 viewport->UpdateWorkRect();
10803 }
10804 }
10805
10806 //-----------------------------------------------------------------------------
10807 // [SECTION] DOCKING
10808 //-----------------------------------------------------------------------------
10809
10810 // (this section is filled in the 'docking' branch)
10811
10812
10813 //-----------------------------------------------------------------------------
10814 // [SECTION] PLATFORM DEPENDENT HELPERS
10815 //-----------------------------------------------------------------------------
10816
10817 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10818
10819 #ifdef _MSC_VER
10820 #pragma comment(lib, "user32")
10821 #pragma comment(lib, "kernel32")
10822 #endif
10823
10824 // Win32 clipboard implementation
10825 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)10826 static const char* GetClipboardTextFn_DefaultImpl(void*)
10827 {
10828 ImGuiContext& g = *GImGui;
10829 g.ClipboardHandlerData.clear();
10830 if (!::OpenClipboard(NULL))
10831 return NULL;
10832 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
10833 if (wbuf_handle == NULL)
10834 {
10835 ::CloseClipboard();
10836 return NULL;
10837 }
10838 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
10839 {
10840 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
10841 g.ClipboardHandlerData.resize(buf_len);
10842 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
10843 }
10844 ::GlobalUnlock(wbuf_handle);
10845 ::CloseClipboard();
10846 return g.ClipboardHandlerData.Data;
10847 }
10848
SetClipboardTextFn_DefaultImpl(void *,const char * text)10849 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10850 {
10851 if (!::OpenClipboard(NULL))
10852 return;
10853 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
10854 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
10855 if (wbuf_handle == NULL)
10856 {
10857 ::CloseClipboard();
10858 return;
10859 }
10860 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
10861 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
10862 ::GlobalUnlock(wbuf_handle);
10863 ::EmptyClipboard();
10864 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
10865 ::GlobalFree(wbuf_handle);
10866 ::CloseClipboard();
10867 }
10868
10869 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
10870
10871 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
10872 static PasteboardRef main_clipboard = 0;
10873
10874 // OSX clipboard implementation
10875 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)10876 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10877 {
10878 if (!main_clipboard)
10879 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10880 PasteboardClear(main_clipboard);
10881 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
10882 if (cf_data)
10883 {
10884 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
10885 CFRelease(cf_data);
10886 }
10887 }
10888
GetClipboardTextFn_DefaultImpl(void *)10889 static const char* GetClipboardTextFn_DefaultImpl(void*)
10890 {
10891 if (!main_clipboard)
10892 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10893 PasteboardSynchronize(main_clipboard);
10894
10895 ItemCount item_count = 0;
10896 PasteboardGetItemCount(main_clipboard, &item_count);
10897 for (ItemCount i = 0; i < item_count; i++)
10898 {
10899 PasteboardItemID item_id = 0;
10900 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
10901 CFArrayRef flavor_type_array = 0;
10902 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
10903 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
10904 {
10905 CFDataRef cf_data;
10906 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
10907 {
10908 ImGuiContext& g = *GImGui;
10909 g.ClipboardHandlerData.clear();
10910 int length = (int)CFDataGetLength(cf_data);
10911 g.ClipboardHandlerData.resize(length + 1);
10912 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
10913 g.ClipboardHandlerData[length] = 0;
10914 CFRelease(cf_data);
10915 return g.ClipboardHandlerData.Data;
10916 }
10917 }
10918 }
10919 return NULL;
10920 }
10921
10922 #else
10923
10924 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)10925 static const char* GetClipboardTextFn_DefaultImpl(void*)
10926 {
10927 ImGuiContext& g = *GImGui;
10928 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
10929 }
10930
SetClipboardTextFn_DefaultImpl(void *,const char * text)10931 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10932 {
10933 ImGuiContext& g = *GImGui;
10934 g.ClipboardHandlerData.clear();
10935 const char* text_end = text + strlen(text);
10936 g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
10937 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
10938 g.ClipboardHandlerData[(int)(text_end - text)] = 0;
10939 }
10940
10941 #endif
10942
10943 // Win32 API IME support (for Asian languages, etc.)
10944 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10945
10946 #include <imm.h>
10947 #ifdef _MSC_VER
10948 #pragma comment(lib, "imm32")
10949 #endif
10950
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)10951 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10952 {
10953 // Notify OS Input Method Editor of text input position
10954 ImGuiIO& io = ImGui::GetIO();
10955 if (HWND hwnd = (HWND)io.ImeWindowHandle)
10956 if (HIMC himc = ::ImmGetContext(hwnd))
10957 {
10958 COMPOSITIONFORM cf;
10959 cf.ptCurrentPos.x = x;
10960 cf.ptCurrentPos.y = y;
10961 cf.dwStyle = CFS_FORCE_POSITION;
10962 ::ImmSetCompositionWindow(himc, &cf);
10963 ::ImmReleaseContext(hwnd, himc);
10964 }
10965 }
10966
10967 #else
10968
ImeSetInputScreenPosFn_DefaultImpl(int,int)10969 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10970
10971 #endif
10972
10973 //-----------------------------------------------------------------------------
10974 // [SECTION] METRICS/DEBUGGER WINDOW
10975 //-----------------------------------------------------------------------------
10976 // - RenderViewportThumbnail() [Internal]
10977 // - RenderViewportsThumbnails() [Internal]
10978 // - MetricsHelpMarker() [Internal]
10979 // - ShowMetricsWindow()
10980 // - DebugNodeColumns() [Internal]
10981 // - DebugNodeDrawList() [Internal]
10982 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
10983 // - DebugNodeStorage() [Internal]
10984 // - DebugNodeTabBar() [Internal]
10985 // - DebugNodeViewport() [Internal]
10986 // - DebugNodeWindow() [Internal]
10987 // - DebugNodeWindowSettings() [Internal]
10988 // - DebugNodeWindowsList() [Internal]
10989 //-----------------------------------------------------------------------------
10990
10991 #ifndef IMGUI_DISABLE_METRICS_WINDOW
10992
DebugRenderViewportThumbnail(ImDrawList * draw_list,ImGuiViewportP * viewport,const ImRect & bb)10993 void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
10994 {
10995 ImGuiContext& g = *GImGui;
10996 ImGuiWindow* window = g.CurrentWindow;
10997
10998 ImVec2 scale = bb.GetSize() / viewport->Size;
10999 ImVec2 off = bb.Min - viewport->Pos * scale;
11000 float alpha_mul = 1.0f;
11001 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
11002 for (int i = 0; i != g.Windows.Size; i++)
11003 {
11004 ImGuiWindow* thumb_window = g.Windows[i];
11005 if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
11006 continue;
11007
11008 ImRect thumb_r = thumb_window->Rect();
11009 ImRect title_r = thumb_window->TitleBarRect();
11010 thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale));
11011 title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height
11012 thumb_r.ClipWithFull(bb);
11013 title_r.ClipWithFull(bb);
11014 const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
11015 window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
11016 window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
11017 window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
11018 window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
11019 }
11020 draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
11021 }
11022
RenderViewportsThumbnails()11023 static void RenderViewportsThumbnails()
11024 {
11025 ImGuiContext& g = *GImGui;
11026 ImGuiWindow* window = g.CurrentWindow;
11027
11028 // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports.
11029 float SCALE = 1.0f / 8.0f;
11030 ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
11031 for (int n = 0; n < g.Viewports.Size; n++)
11032 bb_full.Add(g.Viewports[n]->GetMainRect());
11033 ImVec2 p = window->DC.CursorPos;
11034 ImVec2 off = p - bb_full.Min * SCALE;
11035 for (int n = 0; n < g.Viewports.Size; n++)
11036 {
11037 ImGuiViewportP* viewport = g.Viewports[n];
11038 ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
11039 ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
11040 }
11041 ImGui::Dummy(bb_full.GetSize() * SCALE);
11042 }
11043
11044 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)11045 static void MetricsHelpMarker(const char* desc)
11046 {
11047 ImGui::TextDisabled("(?)");
11048 if (ImGui::IsItemHovered())
11049 {
11050 ImGui::BeginTooltip();
11051 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
11052 ImGui::TextUnformatted(desc);
11053 ImGui::PopTextWrapPos();
11054 ImGui::EndTooltip();
11055 }
11056 }
11057
11058 #ifndef IMGUI_DISABLE_DEMO_WINDOWS
11059 namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); }
11060 #endif
11061
ShowMetricsWindow(bool * p_open)11062 void ImGui::ShowMetricsWindow(bool* p_open)
11063 {
11064 if (!Begin("Dear ImGui Metrics/Debugger", p_open))
11065 {
11066 End();
11067 return;
11068 }
11069
11070 ImGuiContext& g = *GImGui;
11071 ImGuiIO& io = g.IO;
11072 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
11073
11074 // Basic info
11075 Text("Dear ImGui %s", GetVersion());
11076 Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
11077 Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
11078 Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
11079 Text("%d active allocations", io.MetricsActiveAllocations);
11080 //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
11081
11082 Separator();
11083
11084 // Debugging enums
11085 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
11086 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
11087 enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
11088 const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
11089 if (cfg->ShowWindowsRectsType < 0)
11090 cfg->ShowWindowsRectsType = WRT_WorkRect;
11091 if (cfg->ShowTablesRectsType < 0)
11092 cfg->ShowTablesRectsType = TRT_WorkRect;
11093
11094 struct Funcs
11095 {
11096 static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
11097 {
11098 if (rect_type == TRT_OuterRect) { return table->OuterRect; }
11099 else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
11100 else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
11101 else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
11102 else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
11103 else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
11104 else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); }
11105 else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
11106 else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
11107 else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate
11108 else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
11109 else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); }
11110 else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
11111 IM_ASSERT(0);
11112 return ImRect();
11113 }
11114
11115 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
11116 {
11117 if (rect_type == WRT_OuterRect) { return window->Rect(); }
11118 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
11119 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
11120 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
11121 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
11122 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
11123 else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
11124 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
11125 IM_ASSERT(0);
11126 return ImRect();
11127 }
11128 };
11129
11130 // Tools
11131 if (TreeNode("Tools"))
11132 {
11133 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
11134 if (Button("Item Picker.."))
11135 DebugStartItemPicker();
11136 SameLine();
11137 MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
11138
11139 Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
11140 Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
11141 SameLine();
11142 SetNextItemWidth(GetFontSize() * 12);
11143 cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
11144 if (cfg->ShowWindowsRects && g.NavWindow != NULL)
11145 {
11146 BulletText("'%s':", g.NavWindow->Name);
11147 Indent();
11148 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
11149 {
11150 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
11151 Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
11152 }
11153 Unindent();
11154 }
11155 Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
11156 Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
11157
11158 Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
11159 SameLine();
11160 SetNextItemWidth(GetFontSize() * 12);
11161 cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
11162 if (cfg->ShowTablesRects && g.NavWindow != NULL)
11163 {
11164 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
11165 {
11166 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
11167 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
11168 continue;
11169
11170 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
11171 if (IsItemHovered())
11172 GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11173 Indent();
11174 char buf[128];
11175 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
11176 {
11177 if (rect_n >= TRT_ColumnsRect)
11178 {
11179 if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
11180 continue;
11181 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
11182 {
11183 ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
11184 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
11185 Selectable(buf);
11186 if (IsItemHovered())
11187 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11188 }
11189 }
11190 else
11191 {
11192 ImRect r = Funcs::GetTableRect(table, rect_n, -1);
11193 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
11194 Selectable(buf);
11195 if (IsItemHovered())
11196 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11197 }
11198 }
11199 Unindent();
11200 }
11201 }
11202
11203 TreePop();
11204 }
11205
11206 // Windows
11207 DebugNodeWindowsList(&g.Windows, "Windows");
11208 //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder");
11209
11210 // DrawLists
11211 int drawlist_count = 0;
11212 for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
11213 drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
11214 if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
11215 {
11216 for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
11217 {
11218 ImGuiViewportP* viewport = g.Viewports[viewport_i];
11219 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
11220 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
11221 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
11222 }
11223 TreePop();
11224 }
11225
11226 // Viewports
11227 if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
11228 {
11229 Indent(GetTreeNodeToLabelSpacing());
11230 RenderViewportsThumbnails();
11231 Unindent(GetTreeNodeToLabelSpacing());
11232 for (int i = 0; i < g.Viewports.Size; i++)
11233 DebugNodeViewport(g.Viewports[i]);
11234 TreePop();
11235 }
11236
11237 // Details for Popups
11238 if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
11239 {
11240 for (int i = 0; i < g.OpenPopupStack.Size; i++)
11241 {
11242 ImGuiWindow* window = g.OpenPopupStack[i].Window;
11243 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" : "");
11244 }
11245 TreePop();
11246 }
11247
11248 // Details for TabBars
11249 if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
11250 {
11251 for (int n = 0; n < g.TabBars.GetMapSize(); n++)
11252 if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
11253 {
11254 PushID(tab_bar);
11255 DebugNodeTabBar(tab_bar, "TabBar");
11256 PopID();
11257 }
11258 TreePop();
11259 }
11260
11261 // Details for Tables
11262 if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
11263 {
11264 for (int n = 0; n < g.Tables.GetMapSize(); n++)
11265 if (ImGuiTable* table = g.Tables.TryGetMapData(n))
11266 DebugNodeTable(table);
11267 TreePop();
11268 }
11269
11270 // Details for Fonts
11271 #ifndef IMGUI_DISABLE_DEMO_WINDOWS
11272 ImFontAtlas* atlas = g.IO.Fonts;
11273 if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
11274 {
11275 ShowFontAtlas(atlas);
11276 TreePop();
11277 }
11278 #endif
11279
11280 // Details for Docking
11281 #ifdef IMGUI_HAS_DOCK
11282 if (TreeNode("Docking"))
11283 {
11284 TreePop();
11285 }
11286 #endif // #ifdef IMGUI_HAS_DOCK
11287
11288 // Settings
11289 if (TreeNode("Settings"))
11290 {
11291 if (SmallButton("Clear"))
11292 ClearIniSettings();
11293 SameLine();
11294 if (SmallButton("Save to memory"))
11295 SaveIniSettingsToMemory();
11296 SameLine();
11297 if (SmallButton("Save to disk"))
11298 SaveIniSettingsToDisk(g.IO.IniFilename);
11299 SameLine();
11300 if (g.IO.IniFilename)
11301 Text("\"%s\"", g.IO.IniFilename);
11302 else
11303 TextUnformatted("<NULL>");
11304 Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
11305 if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
11306 {
11307 for (int n = 0; n < g.SettingsHandlers.Size; n++)
11308 BulletText("%s", g.SettingsHandlers[n].TypeName);
11309 TreePop();
11310 }
11311 if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
11312 {
11313 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11314 DebugNodeWindowSettings(settings);
11315 TreePop();
11316 }
11317
11318 if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
11319 {
11320 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
11321 DebugNodeTableSettings(settings);
11322 TreePop();
11323 }
11324
11325 #ifdef IMGUI_HAS_DOCK
11326 #endif // #ifdef IMGUI_HAS_DOCK
11327
11328 if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
11329 {
11330 InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
11331 TreePop();
11332 }
11333 TreePop();
11334 }
11335
11336 // Misc Details
11337 if (TreeNode("Internal state"))
11338 {
11339 const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
11340
11341 Text("WINDOWING");
11342 Indent();
11343 Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
11344 Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
11345 Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
11346 Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
11347 Unindent();
11348
11349 Text("ITEMS");
11350 Indent();
11351 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]);
11352 Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
11353 Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask);
11354 Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
11355 Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
11356 Unindent();
11357
11358 Text("NAV,FOCUS");
11359 Indent();
11360 Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
11361 Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
11362 Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
11363 Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
11364 Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
11365 Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
11366 Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
11367 Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
11368 Unindent();
11369
11370 TreePop();
11371 }
11372
11373 // Overlay: Display windows Rectangles and Begin Order
11374 if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
11375 {
11376 for (int n = 0; n < g.Windows.Size; n++)
11377 {
11378 ImGuiWindow* window = g.Windows[n];
11379 if (!window->WasActive)
11380 continue;
11381 ImDrawList* draw_list = GetForegroundDrawList(window);
11382 if (cfg->ShowWindowsRects)
11383 {
11384 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
11385 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
11386 }
11387 if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
11388 {
11389 char buf[32];
11390 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
11391 float font_size = GetFontSize();
11392 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
11393 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
11394 }
11395 }
11396 }
11397
11398 // Overlay: Display Tables Rectangles
11399 if (cfg->ShowTablesRects)
11400 {
11401 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
11402 {
11403 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
11404 if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
11405 continue;
11406 ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
11407 if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
11408 {
11409 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
11410 {
11411 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
11412 ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
11413 float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
11414 draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
11415 }
11416 }
11417 else
11418 {
11419 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
11420 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
11421 }
11422 }
11423 }
11424
11425 #ifdef IMGUI_HAS_DOCK
11426 // Overlay: Display Docking info
11427 if (show_docking_nodes && g.IO.KeyCtrl)
11428 {
11429 }
11430 #endif // #ifdef IMGUI_HAS_DOCK
11431
11432 End();
11433 }
11434
11435 // [DEBUG] List fonts in a font atlas and display its texture
ShowFontAtlas(ImFontAtlas * atlas)11436 void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
11437 {
11438 for (int i = 0; i < atlas->Fonts.Size; i++)
11439 {
11440 ImFont* font = atlas->Fonts[i];
11441 PushID(font);
11442 DebugNodeFont(font);
11443 PopID();
11444 }
11445 if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
11446 {
11447 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
11448 ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
11449 Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
11450 TreePop();
11451 }
11452 }
11453
11454 // [DEBUG] Display contents of Columns
DebugNodeColumns(ImGuiOldColumns * columns)11455 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
11456 {
11457 if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
11458 return;
11459 BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
11460 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
11461 BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
11462 TreePop();
11463 }
11464
11465 // [DEBUG] Display contents of ImDrawList
DebugNodeDrawList(ImGuiWindow * window,const ImDrawList * draw_list,const char * label)11466 void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
11467 {
11468 ImGuiContext& g = *GImGui;
11469 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
11470 int cmd_count = draw_list->CmdBuffer.Size;
11471 if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
11472 cmd_count--;
11473 bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);
11474 if (draw_list == GetWindowDrawList())
11475 {
11476 SameLine();
11477 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)
11478 if (node_open)
11479 TreePop();
11480 return;
11481 }
11482
11483 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
11484 if (window && IsItemHovered())
11485 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11486 if (!node_open)
11487 return;
11488
11489 if (window && !window->WasActive)
11490 TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
11491
11492 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
11493 {
11494 if (pcmd->UserCallback)
11495 {
11496 BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
11497 continue;
11498 }
11499
11500 char buf[300];
11501 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
11502 pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
11503 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
11504 bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
11505 if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
11506 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
11507 if (!pcmd_node_open)
11508 continue;
11509
11510 // Calculate approximate coverage area (touched pixel count)
11511 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
11512 const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
11513 const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
11514 float total_area = 0.0f;
11515 for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
11516 {
11517 ImVec2 triangle[3];
11518 for (int n = 0; n < 3; n++, idx_n++)
11519 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
11520 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
11521 }
11522
11523 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
11524 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
11525 Selectable(buf);
11526 if (IsItemHovered() && fg_draw_list)
11527 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
11528
11529 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
11530 ImGuiListClipper clipper;
11531 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
11532 while (clipper.Step())
11533 for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
11534 {
11535 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
11536 ImVec2 triangle[3];
11537 for (int n = 0; n < 3; n++, idx_i++)
11538 {
11539 const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
11540 triangle[n] = v.pos;
11541 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
11542 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
11543 }
11544
11545 Selectable(buf, false);
11546 if (fg_draw_list && IsItemHovered())
11547 {
11548 ImDrawListFlags backup_flags = fg_draw_list->Flags;
11549 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
11550 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
11551 fg_draw_list->Flags = backup_flags;
11552 }
11553 }
11554 TreePop();
11555 }
11556 TreePop();
11557 }
11558
11559 // [DEBUG] Display mesh/aabb of a ImDrawCmd
DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList * out_draw_list,const ImDrawList * draw_list,const ImDrawCmd * draw_cmd,bool show_mesh,bool show_aabb)11560 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
11561 {
11562 IM_ASSERT(show_mesh || show_aabb);
11563
11564 // Draw wire-frame version of all triangles
11565 ImRect clip_rect = draw_cmd->ClipRect;
11566 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
11567 ImDrawListFlags backup_flags = out_draw_list->Flags;
11568 out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
11569 for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
11570 {
11571 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
11572 ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
11573
11574 ImVec2 triangle[3];
11575 for (int n = 0; n < 3; n++, idx_n++)
11576 vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
11577 if (show_mesh)
11578 out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
11579 }
11580 // Draw bounding boxes
11581 if (show_aabb)
11582 {
11583 out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
11584 out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
11585 }
11586 out_draw_list->Flags = backup_flags;
11587 }
11588
11589 // [DEBUG] Display details for a single font, called by ShowStyleEditor().
DebugNodeFont(ImFont * font)11590 void ImGui::DebugNodeFont(ImFont* font)
11591 {
11592 bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
11593 font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
11594 SameLine();
11595 if (SmallButton("Set as default"))
11596 GetIO().FontDefault = font;
11597 if (!opened)
11598 return;
11599
11600 // Display preview text
11601 PushFont(font);
11602 Text("The quick brown fox jumps over the lazy dog");
11603 PopFont();
11604
11605 // Display details
11606 SetNextItemWidth(GetFontSize() * 8);
11607 DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
11608 SameLine(); MetricsHelpMarker(
11609 "Note than the default embedded font is NOT meant to be scaled.\n\n"
11610 "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
11611 "You may oversample them to get some flexibility with scaling. "
11612 "You can also render at multiple sizes and select which one to use at runtime.\n\n"
11613 "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
11614 Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
11615 char c_str[5];
11616 Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
11617 Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
11618 const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
11619 Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
11620 for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
11621 if (font->ConfigData)
11622 if (const ImFontConfig* cfg = &font->ConfigData[config_i])
11623 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
11624 config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
11625
11626 // Display all glyphs of the fonts in separate pages of 256 characters
11627 if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
11628 {
11629 ImDrawList* draw_list = GetWindowDrawList();
11630 const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
11631 const float cell_size = font->FontSize * 1;
11632 const float cell_spacing = GetStyle().ItemSpacing.y;
11633 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
11634 {
11635 // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
11636 // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
11637 // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
11638 if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
11639 {
11640 base += 4096 - 256;
11641 continue;
11642 }
11643
11644 int count = 0;
11645 for (unsigned int n = 0; n < 256; n++)
11646 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
11647 count++;
11648 if (count <= 0)
11649 continue;
11650 if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
11651 continue;
11652
11653 // Draw a 16x16 grid of glyphs
11654 ImVec2 base_pos = GetCursorScreenPos();
11655 for (unsigned int n = 0; n < 256; n++)
11656 {
11657 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
11658 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
11659 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
11660 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
11661 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
11662 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
11663 if (glyph)
11664 font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
11665 if (glyph && IsMouseHoveringRect(cell_p1, cell_p2))
11666 {
11667 BeginTooltip();
11668 Text("Codepoint: U+%04X", base + n);
11669 Separator();
11670 Text("Visible: %d", glyph->Visible);
11671 Text("AdvanceX: %.1f", glyph->AdvanceX);
11672 Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
11673 Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
11674 EndTooltip();
11675 }
11676 }
11677 Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
11678 TreePop();
11679 }
11680 TreePop();
11681 }
11682 TreePop();
11683 }
11684
11685 // [DEBUG] Display contents of ImGuiStorage
DebugNodeStorage(ImGuiStorage * storage,const char * label)11686 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
11687 {
11688 if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
11689 return;
11690 for (int n = 0; n < storage->Data.Size; n++)
11691 {
11692 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
11693 BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
11694 }
11695 TreePop();
11696 }
11697
11698 // [DEBUG] Display contents of ImGuiTabBar
DebugNodeTabBar(ImGuiTabBar * tab_bar,const char * label)11699 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
11700 {
11701 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
11702 char buf[256];
11703 char* p = buf;
11704 const char* buf_end = buf + IM_ARRAYSIZE(buf);
11705 const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
11706 p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
11707 p += ImFormatString(p, buf_end - p, " { ");
11708 for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
11709 {
11710 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11711 p += ImFormatString(p, buf_end - p, "%s'%s'",
11712 tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???");
11713 }
11714 p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
11715 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11716 bool open = TreeNode(label, "%s", buf);
11717 if (!is_active) { PopStyleColor(); }
11718 if (is_active && IsItemHovered())
11719 {
11720 ImDrawList* draw_list = GetForegroundDrawList();
11721 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
11722 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11723 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
11724 }
11725 if (open)
11726 {
11727 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
11728 {
11729 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11730 PushID(tab);
11731 if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
11732 if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
11733 Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
11734 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
11735 PopID();
11736 }
11737 TreePop();
11738 }
11739 }
11740
DebugNodeViewport(ImGuiViewportP * viewport)11741 void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
11742 {
11743 SetNextItemOpen(true, ImGuiCond_Once);
11744 if (TreeNode("viewport0", "Viewport #%d", 0))
11745 {
11746 ImGuiWindowFlags flags = viewport->Flags;
11747 BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
11748 viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
11749 viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y);
11750 BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
11751 (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
11752 (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
11753 (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
11754 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
11755 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
11756 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
11757 TreePop();
11758 }
11759 }
11760
DebugNodeWindow(ImGuiWindow * window,const char * label)11761 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
11762 {
11763 if (window == NULL)
11764 {
11765 BulletText("%s: NULL", label);
11766 return;
11767 }
11768
11769 ImGuiContext& g = *GImGui;
11770 const bool is_active = window->WasActive;
11771 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
11772 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11773 const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
11774 if (!is_active) { PopStyleColor(); }
11775 if (IsItemHovered() && is_active)
11776 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11777 if (!open)
11778 return;
11779
11780 if (window->MemoryCompacted)
11781 TextDisabled("Note: some memory buffers have been compacted/freed.");
11782
11783 ImGuiWindowFlags flags = window->Flags;
11784 DebugNodeDrawList(window, window->DrawList, "DrawList");
11785 BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
11786 BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
11787 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
11788 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
11789 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
11790 BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
11791 BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
11792 BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
11793 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
11794 {
11795 ImRect r = window->NavRectRel[layer];
11796 if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
11797 {
11798 BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
11799 continue;
11800 }
11801 BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
11802 if (IsItemHovered())
11803 GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255));
11804 }
11805 BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
11806 if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
11807 if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
11808 if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
11809 if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
11810 {
11811 for (int n = 0; n < window->ColumnsStorage.Size; n++)
11812 DebugNodeColumns(&window->ColumnsStorage[n]);
11813 TreePop();
11814 }
11815 DebugNodeStorage(&window->StateStorage, "Storage");
11816 TreePop();
11817 }
11818
DebugNodeWindowSettings(ImGuiWindowSettings * settings)11819 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
11820 {
11821 Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
11822 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
11823 }
11824
DebugNodeWindowsList(ImVector<ImGuiWindow * > * windows,const char * label)11825 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
11826 {
11827 if (!TreeNode(label, "%s (%d)", label, windows->Size))
11828 return;
11829 Text("(In front-to-back order:)");
11830 for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
11831 {
11832 PushID((*windows)[i]);
11833 DebugNodeWindow((*windows)[i], "Window");
11834 PopID();
11835 }
11836 TreePop();
11837 }
11838
11839 #else
11840
ShowMetricsWindow(bool *)11841 void ImGui::ShowMetricsWindow(bool*) {}
ShowFontAtlas(ImFontAtlas *)11842 void ImGui::ShowFontAtlas(ImFontAtlas*) {}
DebugNodeColumns(ImGuiOldColumns *)11843 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
DebugNodeDrawList(ImGuiWindow *,const ImDrawList *,const char *)11844 void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList *,const ImDrawList *,const ImDrawCmd *,bool,bool)11845 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
DebugNodeFont(ImFont *)11846 void ImGui::DebugNodeFont(ImFont*) {}
DebugNodeStorage(ImGuiStorage *,const char *)11847 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
DebugNodeTabBar(ImGuiTabBar *,const char *)11848 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
DebugNodeWindow(ImGuiWindow *,const char *)11849 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
DebugNodeWindowSettings(ImGuiWindowSettings *)11850 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
DebugNodeWindowsList(ImVector<ImGuiWindow * > *,const char *)11851 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
DebugNodeViewport(ImGuiViewportP *)11852 void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
11853
11854 #endif
11855
11856 //-----------------------------------------------------------------------------
11857
11858 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11859 // 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.
11860 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11861 #include "imgui_user.inl"
11862 #endif
11863
11864 //-----------------------------------------------------------------------------
11865
11866 #endif // #ifndef IMGUI_DISABLE
11867