1// dear imgui, v1.84 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/3793 (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// - Discussions https://github.com/ocornut/imgui/discussions
19
20// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
21// See LICENSE.txt for copyright and licensing details (standard MIT License).
22// This library is free but needs your support to sustain development and maintenance.
23// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com".
24// Individuals: you can support continued development via donations. See docs/README or web page.
25
26// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
27// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
28// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
29// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
30// to a better solution or official support for them.
31
32/*
33
34Index of this file:
35
36DOCUMENTATION
37
38- MISSION STATEMENT
39- END-USER GUIDE
40- PROGRAMMER GUIDE
41 - READ FIRST
42 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
43 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
44 - HOW A SIMPLE APPLICATION MAY LOOK LIKE
45 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
46 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
47- API BREAKING CHANGES (read me when you update!)
48- FREQUENTLY ASKED QUESTIONS (FAQ)
49 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
50
51CODE
52(search for "[SECTION]" in the code to find them)
53
54// [SECTION] INCLUDES
55// [SECTION] FORWARD DECLARATIONS
56// [SECTION] CONTEXT AND MEMORY ALLOCATORS
57// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
58// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
59// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
60// [SECTION] MISC HELPERS/UTILITIES (File functions)
61// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
62// [SECTION] MISC HELPERS/UTILITIES (Color functions)
63// [SECTION] ImGuiStorage
64// [SECTION] ImGuiTextFilter
65// [SECTION] ImGuiTextBuffer
66// [SECTION] ImGuiListClipper
67// [SECTION] STYLING
68// [SECTION] RENDER HELPERS
69// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
70// [SECTION] ERROR CHECKING
71// [SECTION] LAYOUT
72// [SECTION] SCROLLING
73// [SECTION] TOOLTIPS
74// [SECTION] POPUPS
75// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
76// [SECTION] DRAG AND DROP
77// [SECTION] LOGGING/CAPTURING
78// [SECTION] SETTINGS
79// [SECTION] VIEWPORTS
80// [SECTION] PLATFORM DEPENDENT HELPERS
81// [SECTION] METRICS/DEBUGGER WINDOW
82
83*/
84
85//-----------------------------------------------------------------------------
86// DOCUMENTATION
87//-----------------------------------------------------------------------------
88
89/*
90
91 MISSION STATEMENT
92 =================
93
94 - Easy to use to create code-driven and data-driven tools.
95 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
96 - Easy to hack and improve.
97 - Minimize setup and maintenance.
98 - Minimize state storage on user side.
99 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
100 - Efficient runtime and memory consumption.
101
102 Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes:
103
104 - Doesn't look fancy, doesn't animate.
105 - Limited layout features, intricate layouts are typically crafted in code.
106
107
108 END-USER GUIDE
109 ==============
110
111 - Double-click on title bar to collapse window.
112 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
113 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
114 - Click and drag on any empty space to move window.
115 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
116 - CTRL+Click on a slider or drag box to input value as text.
117 - Use mouse wheel to scroll.
118 - Text editor:
119 - Hold SHIFT or use mouse to select text.
120 - CTRL+Left/Right to word jump.
121 - CTRL+Shift+Left/Right to select words.
122 - CTRL+A our Double-Click to select all.
123 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
124 - CTRL+Z,CTRL+Y to undo/redo.
125 - ESCAPE to revert text to its original value.
126 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
127 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
128 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
129 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets
130
131
132 PROGRAMMER GUIDE
133 ================
134
135 READ FIRST
136 ----------
137 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
138 - 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
139 destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs.
140 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
141 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
142 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
143 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
144 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
145 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,
146 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
147 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
148 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
149 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
150 If you get an assert, read the messages and comments around the assert.
151 - 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.
152 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
153 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
154 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
155 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
156
157
158 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
159 ----------------------------------------------
160 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
161 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
162 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
163 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
164 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
165 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
166 likely be a comment about it. Please report any issue to the GitHub page!
167 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
168 - Try to keep your copy of Dear ImGui reasonably up to date.
169
170
171 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
172 ---------------------------------------------------------------
173 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
174 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
175 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
176 It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
177 - 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.
178 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
179 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
180 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
181 phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
182 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
183 - 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.
184
185
186 HOW A SIMPLE APPLICATION MAY LOOK LIKE
187 --------------------------------------
188 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
189 The sub-folders in examples/ contain examples applications following this structure.
190
191 // Application init: create a dear imgui context, setup some options, load fonts
192 ImGui::CreateContext();
193 ImGuiIO& io = ImGui::GetIO();
194 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
195 // TODO: Fill optional fields of the io structure later.
196 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
197
198 // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
199 ImGui_ImplWin32_Init(hwnd);
200 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
201
202 // Application main loop
203 while (true)
204 {
205 // Feed inputs to dear imgui, start new frame
206 ImGui_ImplDX11_NewFrame();
207 ImGui_ImplWin32_NewFrame();
208 ImGui::NewFrame();
209
210 // Any application code here
211 ImGui::Text("Hello, world!");
212
213 // Render dear imgui into screen
214 ImGui::Render();
215 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
216 g_pSwapChain->Present(1, 0);
217 }
218
219 // Shutdown
220 ImGui_ImplDX11_Shutdown();
221 ImGui_ImplWin32_Shutdown();
222 ImGui::DestroyContext();
223
224 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE
225
226 // Application init: create a dear imgui context, setup some options, load fonts
227 ImGui::CreateContext();
228 ImGuiIO& io = ImGui::GetIO();
229 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
230 // TODO: Fill optional fields of the io structure later.
231 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
232
233 // Build and load the texture atlas into a texture
234 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
235 int width, height;
236 unsigned char* pixels = NULL;
237 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
238
239 // At this point you've got the texture data and you need to upload that to your graphic system:
240 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
241 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
242 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
243 io.Fonts->SetTexID((void*)texture);
244
245 // Application main loop
246 while (true)
247 {
248 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
249 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
250 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
251 io.DisplaySize.x = 1920.0f; // set the current display width
252 io.DisplaySize.y = 1280.0f; // set the current display height here
253 io.MousePos = my_mouse_pos; // set the mouse position
254 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
255 io.MouseDown[1] = my_mouse_buttons[1];
256
257 // Call NewFrame(), after this point you can use ImGui::* functions anytime
258 // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
259 ImGui::NewFrame();
260
261 // Most of your application code here
262 ImGui::Text("Hello, world!");
263 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
264 MyGameRender(); // may use any Dear ImGui functions as well!
265
266 // Render dear imgui, swap buffers
267 // (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)
268 ImGui::EndFrame();
269 ImGui::Render();
270 ImDrawData* draw_data = ImGui::GetDrawData();
271 MyImGuiRenderFunction(draw_data);
272 SwapBuffers();
273 }
274
275 // Shutdown
276 ImGui::DestroyContext();
277
278 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
279 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
280 Please read the FAQ and example applications for details about this!
281
282
283 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
284 ---------------------------------------------
285 The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.
286
287 void void MyImGuiRenderFunction(ImDrawData* draw_data)
288 {
289 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
290 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
291 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
292 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
293 for (int n = 0; n < draw_data->CmdListsCount; n++)
294 {
295 const ImDrawList* cmd_list = draw_data->CmdLists[n];
296 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
297 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
298 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
299 {
300 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
301 if (pcmd->UserCallback)
302 {
303 pcmd->UserCallback(cmd_list, pcmd);
304 }
305 else
306 {
307 // The texture for the draw call is specified by pcmd->GetTexID().
308 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
309 MyEngineBindTexture((MyTexture*)pcmd->GetTexID());
310
311 // We are using scissoring to clip some objects. All low-level graphics API should support it.
312 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
313 // (some elements visible outside their bounds) but you can fix that once everything else works!
314 // - Clipping coordinates are provided in imgui coordinates space:
315 // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size
316 // - 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.
317 // - In the interest of supporting multi-viewport applications (see 'docking' branch on github),
318 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
319 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
320 ImVec2 pos = draw_data->DisplayPos;
321 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));
322
323 // Render 'pcmd->ElemCount/3' indexed triangles.
324 // 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.
325 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
326 }
327 idx_buffer += pcmd->ElemCount;
328 }
329 }
330 }
331
332
333 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
334 ------------------------------------------
335 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
336 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
337 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
338 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
339 - Keyboard:
340 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
341 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
342 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
343 will be set. For more advanced uses, you may want to read from:
344 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
345 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
346 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
347 Please reach out if you think the game vs navigation input sharing could be improved.
348 - Gamepad:
349 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
350 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
351 Note that io.NavInputs[] is cleared by EndFrame().
352 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
353 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
354 - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
355 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.).
356 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets
357 - 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
358 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
359 - Mouse:
360 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
361 - 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.
362 - 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.
363 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
364 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.
365 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.
366 (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!)
367 (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
368 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
369
370
371 API BREAKING CHANGES
372 ====================
373
374 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
375 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.
376 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.
377 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
378
379 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
380 - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
381 - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder
382 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
383 - if you are using official backends from the source tree: you have nothing to do.
384 - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
385 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
386 - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft
387 - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
388 - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc.
389 flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
390 breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
391 - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use)
392 - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use)
393 - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use)
394 - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
395 this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
396 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.
397 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).
398 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
399 - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY()
400 - 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.
401 - 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.
402 - 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'.
403 - 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.
404 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
405 - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
406 - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
407 - 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.
408 - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
409 - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
410 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
411 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
412 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg
413 - ImGuiInputTextCallback -> use ImGuiTextEditCallback
414 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData
415 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
416 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
417 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
418 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
419 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
420 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
421 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend
422 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
423 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
424 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT
425 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT
426 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
427 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
428 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
429 - 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).
430 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
431 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
432 - 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.
433 - 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.
434 - 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!
435 - 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().
436 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
437 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
438 - if you omitted the 'power' parameter (likely!), you are not affected.
439 - 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.
440 - 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.
441 see https://github.com/ocornut/imgui/issues/3361 for all details.
442 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.
443 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
444 - 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.
445 - 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.
446 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
447 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
448 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
449 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
450 - 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.
451 - 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.
452 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
453 - ShowTestWindow() -> use ShowDemoWindow()
454 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
455 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
456 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
457 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
458 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
459 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
460 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
461 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
462 - 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.
463 - 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).
464 - 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.
465 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
466 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
467 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
468 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
469 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
470 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
471 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
472 - ImFont::Glyph -> use ImFontGlyph
473 - 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.
474 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.
475 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).
476 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
477 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
478 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
479 - 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.
480 - 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
481 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.
482 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.
483 Please reach out if you are affected.
484 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
485 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
486 - 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.
487 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
488 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
489 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
490 - 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!
491 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
492 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
493 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
494 - 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.
495 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
496 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
497 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
498 - 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.
499 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
500 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
501 - 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.
502 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
503 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
504 - 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).
505 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
506 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
507 - 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.
508 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
509 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
510 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
511 - 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.).
512 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
513 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
514 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
515 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
516 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
517 - 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.
518 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
519 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.
520 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.
521 - 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",
522 consistent with other functions. Kept redirection functions (will obsolete).
523 - 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.
524 - 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).
525 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
526 - 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.
527 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
528 - 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.
529 - 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.
530 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
531 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
532 - removed Shutdown() function, as DestroyContext() serve this purpose.
533 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
534 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
535 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
536 - 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.
537 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
538 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
539 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
540 - 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.
541 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
542 - 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
543 - 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.
544 - 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.
545 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
546 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
547 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
548 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
549 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
550 - 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.
551 - 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.
552 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.
553 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
554 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
555 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
556 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
557 - 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.
558 - 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.
559 - 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.
560 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
561 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
562 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
563 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
564 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
565 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
566 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
567 - 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).
568 - 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)".
569 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
570 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
571 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
572 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
573 - 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.
574 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
575 - 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.
576 - 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).
577 - 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).
578 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
579 - 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.
580 - 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.
581 - 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))'
582 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
583 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
584 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
585 - 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().
586 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
587 - 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.
588 - 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.
589 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
590 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.
591 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:
592 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); }
593 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.
594 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
595 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
596 - 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).
597 - 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.
598 - 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).
599 - 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)
600 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
601 - 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.
602 - 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.
603 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
604 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
605 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
606 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.
607 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!
608 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
609 - 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.
610 - 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
611 - 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.
612 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
613 - 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.
614 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
615 - 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.
616 - the signature of the io.RenderDrawListsFn handler has changed!
617 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
618 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
619 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
620 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
621 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
622 - 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.
623 - 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!
624 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
625 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
626 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
627 - 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.
628 - 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
629 - 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!
630 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
631 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
632 - 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.
633 - 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.
634 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
635 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
636 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
637 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
638 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
639 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
640 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
641 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
642 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
643 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
644 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
645 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
646 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
647 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
648 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
649 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
650 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
651 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
652 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
653 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
654 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.
655 - 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()
656 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
657 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
658 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
659 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
660 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
661 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
662 - 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)
663 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
664 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
665 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
666 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
667 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
668 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
669
670
671 FREQUENTLY ASKED QUESTIONS (FAQ)
672 ================================
673
674 Read all answers online:
675 https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
676 Read all answers locally (with a text editor or ideally a Markdown viewer):
677 docs/FAQ.md
678 Some answers are copied down here to facilitate searching in code.
679
680 Q&A: Basics
681 ===========
682
683 Q: Where is the documentation?
684 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
685 - Run the examples/ and explore them.
686 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
687 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
688 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
689 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
690 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
691 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
692 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
693 - Your programming IDE is your friend, find the type or function declaration to find comments
694 associated with it.
695
696 Q: What is this library called?
697 Q: Which version should I get?
698 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
699 >> See https://www.dearimgui.org/faq for details.
700
701 Q&A: Integration
702 ================
703
704 Q: How to get started?
705 A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
706
707 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
708 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
709 >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this.
710
711 Q. How can I enable keyboard controls?
712 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
713 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
714 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
715 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
716 >> See https://www.dearimgui.org/faq
717
718 Q&A: Usage
719 ----------
720
721 Q: Why is my widget not reacting when I click on it?
722 Q: How can I have widgets with an empty label?
723 Q: How can I have multiple widgets with the same label?
724 Q: How can I display an image? What is ImTextureID, how does it works?
725 Q: How can I use my own math types instead of ImVec2/ImVec4?
726 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
727 Q: How can I display custom shapes? (using low-level ImDrawList API)
728 >> See https://www.dearimgui.org/faq
729
730 Q&A: Fonts, Text
731 ================
732
733 Q: How should I handle DPI in my application?
734 Q: How can I load a different font than the default?
735 Q: How can I easily use icons in my application?
736 Q: How can I load multiple fonts?
737 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
738 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
739
740 Q&A: Concerns
741 =============
742
743 Q: Who uses Dear ImGui?
744 Q: Can you create elaborate/serious tools with Dear ImGui?
745 Q: Can you reskin the look of Dear ImGui?
746 Q: Why using C++ (as opposed to C)?
747 >> See https://www.dearimgui.org/faq
748
749 Q&A: Community
750 ==============
751
752 Q: How can I help?
753 A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui!
754 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
755 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.
756 - Individuals: you can support continued development via PayPal donations. See README.
757 - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt
758 and see how you want to help and can help!
759 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
760 You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
761 But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
762 - 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).
763
764*/
765
766//-------------------------------------------------------------------------
767// [SECTION] INCLUDES
768//-------------------------------------------------------------------------
769
770#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
771#define _CRT_SECURE_NO_WARNINGS
772#endif
773
774#include "imgui.h"
775#ifndef IMGUI_DISABLE
776
777#ifndef IMGUI_DEFINE_MATH_OPERATORS
778#define IMGUI_DEFINE_MATH_OPERATORS
779#endif
780#include "imgui_internal.h"
781
782// System includes
783#include <ctype.h> // toupper
784#include <stdio.h> // vsnprintf, sscanf, printf
785#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
786#include <stddef.h> // intptr_t
787#else
788#include <stdint.h> // intptr_t
789#endif
790
791// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
792#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
793#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
794#endif
795
796// [Windows] OS specific includes (optional)
797#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)
798#define IMGUI_DISABLE_WIN32_FUNCTIONS
799#endif
800#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
801#ifndef WIN32_LEAN_AND_MEAN
802#define WIN32_LEAN_AND_MEAN
803#endif
804#ifndef NOMINMAX
805#define NOMINMAX
806#endif
807#ifndef __MINGW32__
808#include <Windows.h> // _wfopen, OpenClipboard
809#else
810#include <windows.h>
811#endif
812#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
813#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
814#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
815#endif
816#endif
817
818// [Apple] OS specific includes
819#if defined(__APPLE__)
820#include <TargetConditionals.h>
821#endif
822
823// Visual Studio warnings
824#ifdef _MSC_VER
825#pragma warning (disable: 4127) // condition expression is constant
826#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
827#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
828#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
829#endif
830#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).
831#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
832#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
833#endif
834
835// Clang/GCC warnings with -Weverything
836#if defined(__clang__)
837#if __has_warning("-Wunknown-warning-option")
838#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!
839#endif
840#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
841#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
842#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.
843#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.
844#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.
845#pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
846#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
847#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.
848#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
849#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
850#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.
851#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
852#elif defined(__GNUC__)
853// We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
854#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
855#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
856#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
857#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
858#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
859#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
860#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
861#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
862#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
863#endif
864
865// Debug options
866#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
867#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
868#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
869
870// 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.
871static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
872static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
873
874// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)
875static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().
876static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
877static 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.
878
879//-------------------------------------------------------------------------
880// [SECTION] FORWARD DECLARATIONS
881//-------------------------------------------------------------------------
882
883static void SetCurrentWindow(ImGuiWindow* window);
884static void FindHoveredWindow();
885static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
886static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
887
888static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
889static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
890
891// Settings
892static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
893static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
894static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
895static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
896static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
897
898// Platform Dependents default implementation for IO functions
899static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
900static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
901static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
902
903namespace ImGui
904{
905// Navigation
906static void NavUpdate();
907static void NavUpdateWindowing();
908static void NavUpdateWindowingOverlay();
909static void NavUpdateMoveResult();
910static void NavUpdateInitResult();
911static float NavUpdatePageUpPageDown();
912static inline void NavUpdateAnyRequestFlag();
913static void NavEndFrame();
914static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand);
915static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel);
916static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
917static ImVec2 NavCalcPreferredRefPos();
918static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
919static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
920static void NavRestoreLayer(ImGuiNavLayer layer);
921static int FindWindowFocusIndex(ImGuiWindow* window);
922
923// Error Checking
924static void ErrorCheckNewFrameSanityChecks();
925static void ErrorCheckEndFrameSanityChecks();
926
927// Misc
928static void UpdateSettings();
929static void UpdateMouseInputs();
930static void UpdateMouseWheel();
931static void UpdateTabFocus();
932static void UpdateDebugToolItemPicker();
933static 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);
934static void RenderWindowOuterBorders(ImGuiWindow* window);
935static 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);
936static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
937
938// Viewports
939static void UpdateViewportsNewFrame();
940
941}
942
943//-----------------------------------------------------------------------------
944// [SECTION] CONTEXT AND MEMORY ALLOCATORS
945//-----------------------------------------------------------------------------
946
947// DLL users:
948// - Heaps and globals are not shared across DLL boundaries!
949// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
950// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
951// - 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.
952// - 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).
953
954// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
955// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
956// Change to a different context by calling ImGui::SetCurrentContext().
957// - Important: Dear ImGui functions are not thread-safe because of this pointer.
958// If you want thread-safety to allow N threads to access N different contexts:
959// - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
960// struct ImGuiContext;
961// extern thread_local ImGuiContext* MyImGuiTLS;
962// #define GImGui MyImGuiTLS
963// 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.
964// - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
965// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
966// - DLL users: read comments above.
967#ifndef GImGui
968ImGuiContext* GImGui = NULL;
969#endif
970
971// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
972// - 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.
973// - DLL users: read comments above.
974#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
975static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
976static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
977#else
978static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
979static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
980#endif
981static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper;
982static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper;
983static void* GImAllocatorUserData = NULL;
984
985//-----------------------------------------------------------------------------
986// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
987//-----------------------------------------------------------------------------
988
989ImGuiStyle::ImGuiStyle()
990{
991 Alpha = 1.0f; // Global alpha applies to everything in ImGui
992 WindowPadding = ImVec2(8,8); // Padding within a window
993 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.
994 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
995 WindowMinSize = ImVec2(32,32); // Minimum window size
996 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
997 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
998 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
999 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1000 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1001 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1002 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1003 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1004 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1005 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1006 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1007 CellPadding = ImVec2(4,2); // Padding within a table cell
1008 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!
1009 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1010 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1011 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1012 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1013 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
1014 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1015 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1016 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1017 TabBorderSize = 0.0f; // Thickness of border around tabs.
1018 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.
1019 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1020 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1021 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.
1022 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.
1023 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.
1024 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1025 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1026 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
1027 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1028 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.
1029 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.
1030
1031 // Default theme
1032 ImGui::StyleColorsDark(this);
1033}
1034
1035// 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.
1036// 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.
1037void ImGuiStyle::ScaleAllSizes(float scale_factor)
1038{
1039 WindowPadding = ImFloor(WindowPadding * scale_factor);
1040 WindowRounding = ImFloor(WindowRounding * scale_factor);
1041 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1042 ChildRounding = ImFloor(ChildRounding * scale_factor);
1043 PopupRounding = ImFloor(PopupRounding * scale_factor);
1044 FramePadding = ImFloor(FramePadding * scale_factor);
1045 FrameRounding = ImFloor(FrameRounding * scale_factor);
1046 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1047 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1048 CellPadding = ImFloor(CellPadding * scale_factor);
1049 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1050 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1051 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1052 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1053 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1054 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1055 GrabRounding = ImFloor(GrabRounding * scale_factor);
1056 LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
1057 TabRounding = ImFloor(TabRounding * scale_factor);
1058 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;
1059 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1060 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1061 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1062}
1063
1064ImGuiIO::ImGuiIO()
1065{
1066 // Most fields are initialized with zero
1067 memset(this, 0, sizeof(*this));
1068 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.
1069
1070 // Settings
1071 ConfigFlags = ImGuiConfigFlags_None;
1072 BackendFlags = ImGuiBackendFlags_None;
1073 DisplaySize = ImVec2(-1.0f, -1.0f);
1074 DeltaTime = 1.0f / 60.0f;
1075 IniSavingRate = 5.0f;
1076 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).
1077 LogFilename = "imgui_log.txt";
1078 MouseDoubleClickTime = 0.30f;
1079 MouseDoubleClickMaxDist = 6.0f;
1080 for (int i = 0; i < ImGuiKey_COUNT; i++)
1081 KeyMap[i] = -1;
1082 KeyRepeatDelay = 0.275f;
1083 KeyRepeatRate = 0.050f;
1084 UserData = NULL;
1085
1086 Fonts = NULL;
1087 FontGlobalScale = 1.0f;
1088 FontDefault = NULL;
1089 FontAllowUserScaling = false;
1090 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1091
1092 // Miscellaneous options
1093 MouseDrawCursor = false;
1094#ifdef __APPLE__
1095 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1096#else
1097 ConfigMacOSXBehaviors = false;
1098#endif
1099 ConfigInputTextCursorBlink = true;
1100 ConfigWindowsResizeFromEdges = true;
1101 ConfigWindowsMoveFromTitleBarOnly = false;
1102 ConfigMemoryCompactTimer = 60.0f;
1103
1104 // Platform Functions
1105 BackendPlatformName = BackendRendererName = NULL;
1106 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1107 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1108 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1109 ClipboardUserData = NULL;
1110 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1111 ImeWindowHandle = NULL;
1112
1113 // Input (NB: we already have memset zero the entire structure!)
1114 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1115 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1116 MouseDragThreshold = 6.0f;
1117 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1118 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1119 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1120}
1121
1122// Pass in translated ASCII characters for text input.
1123// - with glfw you can get those from the callback set in glfwSetCharCallback()
1124// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1125void ImGuiIO::AddInputCharacter(unsigned int c)
1126{
1127 if (c != 0)
1128 InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1129}
1130
1131// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1132// we should save the high surrogate.
1133void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1134{
1135 if (c == 0 && InputQueueSurrogate == 0)
1136 return;
1137
1138 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1139 {
1140 if (InputQueueSurrogate != 0)
1141 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1142 InputQueueSurrogate = c;
1143 return;
1144 }
1145
1146 ImWchar cp = c;
1147 if (InputQueueSurrogate != 0)
1148 {
1149 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1150 {
1151 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1152 }
1153 else
1154 {
1155#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1156 cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1157#else
1158 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1159#endif
1160 }
1161
1162 InputQueueSurrogate = 0;
1163 }
1164 InputQueueCharacters.push_back(cp);
1165}
1166
1167void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1168{
1169 while (*utf8_chars != 0)
1170 {
1171 unsigned int c = 0;
1172 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1173 if (c != 0)
1174 InputQueueCharacters.push_back((ImWchar)c);
1175 }
1176}
1177
1178void ImGuiIO::ClearInputCharacters()
1179{
1180 InputQueueCharacters.resize(0);
1181}
1182
1183//-----------------------------------------------------------------------------
1184// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1185//-----------------------------------------------------------------------------
1186
1187ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1188{
1189 IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1190 ImVec2 p_last = p1;
1191 ImVec2 p_closest;
1192 float p_closest_dist2 = FLT_MAX;
1193 float t_step = 1.0f / (float)num_segments;
1194 for (int i_step = 1; i_step <= num_segments; i_step++)
1195 {
1196 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1197 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1198 float dist2 = ImLengthSqr(p - p_line);
1199 if (dist2 < p_closest_dist2)
1200 {
1201 p_closest = p_line;
1202 p_closest_dist2 = dist2;
1203 }
1204 p_last = p_current;
1205 }
1206 return p_closest;
1207}
1208
1209// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1210static 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)
1211{
1212 float dx = x4 - x1;
1213 float dy = y4 - y1;
1214 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1215 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1216 d2 = (d2 >= 0) ? d2 : -d2;
1217 d3 = (d3 >= 0) ? d3 : -d3;
1218 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1219 {
1220 ImVec2 p_current(x4, y4);
1221 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1222 float dist2 = ImLengthSqr(p - p_line);
1223 if (dist2 < p_closest_dist2)
1224 {
1225 p_closest = p_line;
1226 p_closest_dist2 = dist2;
1227 }
1228 p_last = p_current;
1229 }
1230 else if (level < 10)
1231 {
1232 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1233 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1234 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1235 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1236 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1237 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1238 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1239 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1240 }
1241}
1242
1243// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1244// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
1245ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1246{
1247 IM_ASSERT(tess_tol > 0.0f);
1248 ImVec2 p_last = p1;
1249 ImVec2 p_closest;
1250 float p_closest_dist2 = FLT_MAX;
1251 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);
1252 return p_closest;
1253}
1254
1255ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1256{
1257 ImVec2 ap = p - a;
1258 ImVec2 ab_dir = b - a;
1259 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1260 if (dot < 0.0f)
1261 return a;
1262 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1263 if (dot > ab_len_sqr)
1264 return b;
1265 return a + ab_dir * dot / ab_len_sqr;
1266}
1267
1268bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1269{
1270 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1271 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1272 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1273 return ((b1 == b2) && (b2 == b3));
1274}
1275
1276void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1277{
1278 ImVec2 v0 = b - a;
1279 ImVec2 v1 = c - a;
1280 ImVec2 v2 = p - a;
1281 const float denom = v0.x * v1.y - v1.x * v0.y;
1282 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1283 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1284 out_u = 1.0f - out_v - out_w;
1285}
1286
1287ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1288{
1289 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1290 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1291 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1292 float dist2_ab = ImLengthSqr(p - proj_ab);
1293 float dist2_bc = ImLengthSqr(p - proj_bc);
1294 float dist2_ca = ImLengthSqr(p - proj_ca);
1295 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1296 if (m == dist2_ab)
1297 return proj_ab;
1298 if (m == dist2_bc)
1299 return proj_bc;
1300 return proj_ca;
1301}
1302
1303//-----------------------------------------------------------------------------
1304// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1305//-----------------------------------------------------------------------------
1306
1307// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
1308int ImStricmp(const char* str1, const char* str2)
1309{
1310 int d;
1311 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1312 return d;
1313}
1314
1315int ImStrnicmp(const char* str1, const char* str2, size_t count)
1316{
1317 int d = 0;
1318 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1319 return d;
1320}
1321
1322void ImStrncpy(char* dst, const char* src, size_t count)
1323{
1324 if (count < 1)
1325 return;
1326 if (count > 1)
1327 strncpy(dst, src, count - 1);
1328 dst[count - 1] = 0;
1329}
1330
1331char* ImStrdup(const char* str)
1332{
1333 size_t len = strlen(str);
1334 void* buf = IM_ALLOC(len + 1);
1335 return (char*)memcpy(buf, (const void*)str, len + 1);
1336}
1337
1338char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1339{
1340 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1341 size_t src_size = strlen(src) + 1;
1342 if (dst_buf_size < src_size)
1343 {
1344 IM_FREE(dst);
1345 dst = (char*)IM_ALLOC(src_size);
1346 if (p_dst_size)
1347 *p_dst_size = src_size;
1348 }
1349 return (char*)memcpy(dst, (const void*)src, src_size);
1350}
1351
1352const char* ImStrchrRange(const char* str, const char* str_end, char c)
1353{
1354 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1355 return p;
1356}
1357
1358int ImStrlenW(const ImWchar* str)
1359{
1360 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1361 int n = 0;
1362 while (*str++) n++;
1363 return n;
1364}
1365
1366// Find end-of-line. Return pointer will point to either first \n, either str_end.
1367const char* ImStreolRange(const char* str, const char* str_end)
1368{
1369 const char* p = (const char*)memchr(str, '\n', str_end - str);
1370 return p ? p : str_end;
1371}
1372
1373const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1374{
1375 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1376 buf_mid_line--;
1377 return buf_mid_line;
1378}
1379
1380const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1381{
1382 if (!needle_end)
1383 needle_end = needle + strlen(needle);
1384
1385 const char un0 = (char)toupper(*needle);
1386 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1387 {
1388 if (toupper(*haystack) == un0)
1389 {
1390 const char* b = needle + 1;
1391 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1392 if (toupper(*a) != toupper(*b))
1393 break;
1394 if (b == needle_end)
1395 return haystack;
1396 }
1397 haystack++;
1398 }
1399 return NULL;
1400}
1401
1402// 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.
1403void ImStrTrimBlanks(char* buf)
1404{
1405 char* p = buf;
1406 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1407 p++;
1408 char* p_start = p;
1409 while (*p != 0) // Find end of string
1410 p++;
1411 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1412 p--;
1413 if (p_start != buf) // Copy memory if we had leading blanks
1414 memmove(buf, p_start, p - p_start);
1415 buf[p - p_start] = 0; // Zero terminate
1416}
1417
1418const char* ImStrSkipBlank(const char* str)
1419{
1420 while (str[0] == ' ' || str[0] == '\t')
1421 str++;
1422 return str;
1423}
1424
1425// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1426// 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.
1427// B) When buf==NULL vsnprintf() will return the output size.
1428#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1429
1430// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1431// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1432// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1433// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1434#ifdef IMGUI_USE_STB_SPRINTF
1435#define STB_SPRINTF_IMPLEMENTATION
1436#include "stb_sprintf.h"
1437#endif
1438
1439#if defined(_MSC_VER) && !defined(vsnprintf)
1440#define vsnprintf _vsnprintf
1441#endif
1442
1443int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1444{
1445 va_list args;
1446 va_start(args, fmt);
1447#ifdef IMGUI_USE_STB_SPRINTF
1448 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1449#else
1450 int w = vsnprintf(buf, buf_size, fmt, args);
1451#endif
1452 va_end(args);
1453 if (buf == NULL)
1454 return w;
1455 if (w == -1 || w >= (int)buf_size)
1456 w = (int)buf_size - 1;
1457 buf[w] = 0;
1458 return w;
1459}
1460
1461int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1462{
1463#ifdef IMGUI_USE_STB_SPRINTF
1464 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1465#else
1466 int w = vsnprintf(buf, buf_size, fmt, args);
1467#endif
1468 if (buf == NULL)
1469 return w;
1470 if (w == -1 || w >= (int)buf_size)
1471 w = (int)buf_size - 1;
1472 buf[w] = 0;
1473 return w;
1474}
1475#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1476
1477// CRC32 needs a 1KB lookup table (not cache friendly)
1478// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1479// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1480static const ImU32 GCrc32LookupTable[256] =
1481{
1482 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1483 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1484 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1485 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1486 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1487 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1488 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1489 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1490 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1491 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1492 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1493 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1494 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1495 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1496 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1497 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1498};
1499
1500// Known size hash
1501// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1502// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1503ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1504{
1505 ImU32 crc = ~seed;
1506 const unsigned char* data = (const unsigned char*)data_p;
1507 const ImU32* crc32_lut = GCrc32LookupTable;
1508 while (data_size-- != 0)
1509 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1510 return ~crc;
1511}
1512
1513// Zero-terminated string hash, with support for ### to reset back to seed value
1514// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1515// Because this syntax is rarely used we are optimizing for the common case.
1516// - If we reach ### in the string we discard the hash so far and reset to the seed.
1517// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1518// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
1519ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1520{
1521 seed = ~seed;
1522 ImU32 crc = seed;
1523 const unsigned char* data = (const unsigned char*)data_p;
1524 const ImU32* crc32_lut = GCrc32LookupTable;
1525 if (data_size != 0)
1526 {
1527 while (data_size-- != 0)
1528 {
1529 unsigned char c = *data++;
1530 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1531 crc = seed;
1532 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1533 }
1534 }
1535 else
1536 {
1537 while (unsigned char c = *data++)
1538 {
1539 if (c == '#' && data[0] == '#' && data[1] == '#')
1540 crc = seed;
1541 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1542 }
1543 }
1544 return ~crc;
1545}
1546
1547//-----------------------------------------------------------------------------
1548// [SECTION] MISC HELPERS/UTILITIES (File functions)
1549//-----------------------------------------------------------------------------
1550
1551// Default file functions
1552#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1553
1554ImFileHandle ImFileOpen(const char* filename, const char* mode)
1555{
1556#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1557 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1558 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1559 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1560 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1561 ImVector<ImWchar> buf;
1562 buf.resize(filename_wsize + mode_wsize);
1563 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1564 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1565 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1566#else
1567 return fopen(filename, mode);
1568#endif
1569}
1570
1571// 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.
1572bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
1573ImU64 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; }
1574ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
1575ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
1576#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1577
1578// Helper: Load file content into memory
1579// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1580// This can't really be used with "rt" because fseek size won't match read size.
1581void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1582{
1583 IM_ASSERT(filename && mode);
1584 if (out_file_size)
1585 *out_file_size = 0;
1586
1587 ImFileHandle f;
1588 if ((f = ImFileOpen(filename, mode)) == NULL)
1589 return NULL;
1590
1591 size_t file_size = (size_t)ImFileGetSize(f);
1592 if (file_size == (size_t)-1)
1593 {
1594 ImFileClose(f);
1595 return NULL;
1596 }
1597
1598 void* file_data = IM_ALLOC(file_size + padding_bytes);
1599 if (file_data == NULL)
1600 {
1601 ImFileClose(f);
1602 return NULL;
1603 }
1604 if (ImFileRead(file_data, 1, file_size, f) != file_size)
1605 {
1606 ImFileClose(f);
1607 IM_FREE(file_data);
1608 return NULL;
1609 }
1610 if (padding_bytes > 0)
1611 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1612
1613 ImFileClose(f);
1614 if (out_file_size)
1615 *out_file_size = file_size;
1616
1617 return file_data;
1618}
1619
1620//-----------------------------------------------------------------------------
1621// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1622//-----------------------------------------------------------------------------
1623
1624// Convert UTF-8 to 32-bit character, process single character input.
1625// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
1626// We handle UTF-8 decoding error by skipping forward.
1627int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1628{
1629 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 };
1630 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
1631 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
1632 static const int shiftc[] = { 0, 18, 12, 6, 0 };
1633 static const int shifte[] = { 0, 6, 4, 2, 0 };
1634 int len = lengths[*(const unsigned char*)in_text >> 3];
1635 int wanted = len + !len;
1636
1637 if (in_text_end == NULL)
1638 in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
1639
1640 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
1641 // so it is fast even with excessive branching.
1642 unsigned char s[4];
1643 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
1644 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
1645 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
1646 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
1647
1648 // Assume a four-byte character and load four bytes. Unused bits are shifted out.
1649 *out_char = (uint32_t)(s[0] & masks[len]) << 18;
1650 *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
1651 *out_char |= (uint32_t)(s[2] & 0x3f) << 6;
1652 *out_char |= (uint32_t)(s[3] & 0x3f) << 0;
1653 *out_char >>= shiftc[len];
1654
1655 // Accumulate the various error conditions.
1656 int e = 0;
1657 e = (*out_char < mins[len]) << 6; // non-canonical encoding
1658 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half?
1659 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range?
1660 e |= (s[1] & 0xc0) >> 2;
1661 e |= (s[2] & 0xc0) >> 4;
1662 e |= (s[3] ) >> 6;
1663 e ^= 0x2a; // top two bits of each tail byte correct?
1664 e >>= shifte[len];
1665
1666 if (e)
1667 {
1668 // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
1669 // One byte is consumed in case of invalid first byte of in_text.
1670 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
1671 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
1672 wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
1673 *out_char = IM_UNICODE_CODEPOINT_INVALID;
1674 }
1675
1676 return wanted;
1677}
1678
1679int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1680{
1681 ImWchar* buf_out = buf;
1682 ImWchar* buf_end = buf + buf_size;
1683 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1684 {
1685 unsigned int c;
1686 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1687 if (c == 0)
1688 break;
1689 *buf_out++ = (ImWchar)c;
1690 }
1691 *buf_out = 0;
1692 if (in_text_remaining)
1693 *in_text_remaining = in_text;
1694 return (int)(buf_out - buf);
1695}
1696
1697int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1698{
1699 int char_count = 0;
1700 while ((!in_text_end || in_text < in_text_end) && *in_text)
1701 {
1702 unsigned int c;
1703 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1704 if (c == 0)
1705 break;
1706 char_count++;
1707 }
1708 return char_count;
1709}
1710
1711// Based on stb_to_utf8() from github.com/nothings/stb/
1712static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
1713{
1714 if (c < 0x80)
1715 {
1716 buf[0] = (char)c;
1717 return 1;
1718 }
1719 if (c < 0x800)
1720 {
1721 if (buf_size < 2) return 0;
1722 buf[0] = (char)(0xc0 + (c >> 6));
1723 buf[1] = (char)(0x80 + (c & 0x3f));
1724 return 2;
1725 }
1726 if (c < 0x10000)
1727 {
1728 if (buf_size < 3) return 0;
1729 buf[0] = (char)(0xe0 + (c >> 12));
1730 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1731 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1732 return 3;
1733 }
1734 if (c <= 0x10FFFF)
1735 {
1736 if (buf_size < 4) return 0;
1737 buf[0] = (char)(0xf0 + (c >> 18));
1738 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1739 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1740 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1741 return 4;
1742 }
1743 // Invalid code point, the max unicode is 0x10FFFF
1744 return 0;
1745}
1746
1747const char* ImTextCharToUtf8(char out_buf[5], unsigned int c)
1748{
1749 int count = ImTextCharToUtf8_inline(out_buf, 5, c);
1750 out_buf[count] = 0;
1751 return out_buf;
1752}
1753
1754// Not optimal but we very rarely use this function.
1755int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1756{
1757 unsigned int unused = 0;
1758 return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1759}
1760
1761static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1762{
1763 if (c < 0x80) return 1;
1764 if (c < 0x800) return 2;
1765 if (c < 0x10000) return 3;
1766 if (c <= 0x10FFFF) return 4;
1767 return 3;
1768}
1769
1770int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1771{
1772 char* buf_p = out_buf;
1773 const char* buf_end = out_buf + out_buf_size;
1774 while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1775 {
1776 unsigned int c = (unsigned int)(*in_text++);
1777 if (c < 0x80)
1778 *buf_p++ = (char)c;
1779 else
1780 buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
1781 }
1782 *buf_p = 0;
1783 return (int)(buf_p - out_buf);
1784}
1785
1786int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1787{
1788 int bytes_count = 0;
1789 while ((!in_text_end || in_text < in_text_end) && *in_text)
1790 {
1791 unsigned int c = (unsigned int)(*in_text++);
1792 if (c < 0x80)
1793 bytes_count++;
1794 else
1795 bytes_count += ImTextCountUtf8BytesFromChar(c);
1796 }
1797 return bytes_count;
1798}
1799
1800//-----------------------------------------------------------------------------
1801// [SECTION] MISC HELPERS/UTILITIES (Color functions)
1802// Note: The Convert functions are early design which are not consistent with other API.
1803//-----------------------------------------------------------------------------
1804
1805IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1806{
1807 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1808 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1809 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1810 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1811 return IM_COL32(r, g, b, 0xFF);
1812}
1813
1814ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1815{
1816 float s = 1.0f / 255.0f;
1817 return ImVec4(
1818 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1819 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1820 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1821 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1822}
1823
1824ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1825{
1826 ImU32 out;
1827 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1828 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1829 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1830 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1831 return out;
1832}
1833
1834// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1835// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
1836void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1837{
1838 float K = 0.f;
1839 if (g < b)
1840 {
1841 ImSwap(g, b);
1842 K = -1.f;
1843 }
1844 if (r < g)
1845 {
1846 ImSwap(r, g);
1847 K = -2.f / 6.f - K;
1848 }
1849
1850 const float chroma = r - (g < b ? g : b);
1851 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1852 out_s = chroma / (r + 1e-20f);
1853 out_v = r;
1854}
1855
1856// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1857// also http://en.wikipedia.org/wiki/HSL_and_HSV
1858void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1859{
1860 if (s == 0.0f)
1861 {
1862 // gray
1863 out_r = out_g = out_b = v;
1864 return;
1865 }
1866
1867 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1868 int i = (int)h;
1869 float f = h - (float)i;
1870 float p = v * (1.0f - s);
1871 float q = v * (1.0f - s * f);
1872 float t = v * (1.0f - s * (1.0f - f));
1873
1874 switch (i)
1875 {
1876 case 0: out_r = v; out_g = t; out_b = p; break;
1877 case 1: out_r = q; out_g = v; out_b = p; break;
1878 case 2: out_r = p; out_g = v; out_b = t; break;
1879 case 3: out_r = p; out_g = q; out_b = v; break;
1880 case 4: out_r = t; out_g = p; out_b = v; break;
1881 case 5: default: out_r = v; out_g = p; out_b = q; break;
1882 }
1883}
1884
1885//-----------------------------------------------------------------------------
1886// [SECTION] ImGuiStorage
1887// Helper: Key->value storage
1888//-----------------------------------------------------------------------------
1889
1890// std::lower_bound but without the bullshit
1891static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1892{
1893 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1894 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1895 size_t count = (size_t)(last - first);
1896 while (count > 0)
1897 {
1898 size_t count2 = count >> 1;
1899 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1900 if (mid->key < key)
1901 {
1902 first = ++mid;
1903 count -= count2 + 1;
1904 }
1905 else
1906 {
1907 count = count2;
1908 }
1909 }
1910 return first;
1911}
1912
1913// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
1914void ImGuiStorage::BuildSortByKey()
1915{
1916 struct StaticFunc
1917 {
1918 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1919 {
1920 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1921 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1922 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1923 return 0;
1924 }
1925 };
1926 if (Data.Size > 1)
1927 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1928}
1929
1930int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1931{
1932 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1933 if (it == Data.end() || it->key != key)
1934 return default_val;
1935 return it->val_i;
1936}
1937
1938bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1939{
1940 return GetInt(key, default_val ? 1 : 0) != 0;
1941}
1942
1943float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1944{
1945 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1946 if (it == Data.end() || it->key != key)
1947 return default_val;
1948 return it->val_f;
1949}
1950
1951void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1952{
1953 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1954 if (it == Data.end() || it->key != key)
1955 return NULL;
1956 return it->val_p;
1957}
1958
1959// 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.
1960int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1961{
1962 ImGuiStoragePair* it = LowerBound(Data, key);
1963 if (it == Data.end() || it->key != key)
1964 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1965 return &it->val_i;
1966}
1967
1968bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1969{
1970 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1971}
1972
1973float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1974{
1975 ImGuiStoragePair* it = LowerBound(Data, key);
1976 if (it == Data.end() || it->key != key)
1977 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1978 return &it->val_f;
1979}
1980
1981void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1982{
1983 ImGuiStoragePair* it = LowerBound(Data, key);
1984 if (it == Data.end() || it->key != key)
1985 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1986 return &it->val_p;
1987}
1988
1989// 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)
1990void ImGuiStorage::SetInt(ImGuiID key, int val)
1991{
1992 ImGuiStoragePair* it = LowerBound(Data, key);
1993 if (it == Data.end() || it->key != key)
1994 {
1995 Data.insert(it, ImGuiStoragePair(key, val));
1996 return;
1997 }
1998 it->val_i = val;
1999}
2000
2001void ImGuiStorage::SetBool(ImGuiID key, bool val)
2002{
2003 SetInt(key, val ? 1 : 0);
2004}
2005
2006void ImGuiStorage::SetFloat(ImGuiID key, float val)
2007{
2008 ImGuiStoragePair* it = LowerBound(Data, key);
2009 if (it == Data.end() || it->key != key)
2010 {
2011 Data.insert(it, ImGuiStoragePair(key, val));
2012 return;
2013 }
2014 it->val_f = val;
2015}
2016
2017void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2018{
2019 ImGuiStoragePair* it = LowerBound(Data, key);
2020 if (it == Data.end() || it->key != key)
2021 {
2022 Data.insert(it, ImGuiStoragePair(key, val));
2023 return;
2024 }
2025 it->val_p = val;
2026}
2027
2028void ImGuiStorage::SetAllInt(int v)
2029{
2030 for (int i = 0; i < Data.Size; i++)
2031 Data[i].val_i = v;
2032}
2033
2034//-----------------------------------------------------------------------------
2035// [SECTION] ImGuiTextFilter
2036//-----------------------------------------------------------------------------
2037
2038// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2039ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
2040{
2041 if (default_filter)
2042 {
2043 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2044 Build();
2045 }
2046 else
2047 {
2048 InputBuf[0] = 0;
2049 CountGrep = 0;
2050 }
2051}
2052
2053bool ImGuiTextFilter::Draw(const char* label, float width)
2054{
2055 if (width != 0.0f)
2056 ImGui::SetNextItemWidth(width);
2057 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2058 if (value_changed)
2059 Build();
2060 return value_changed;
2061}
2062
2063void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2064{
2065 out->resize(0);
2066 const char* wb = b;
2067 const char* we = wb;
2068 while (we < e)
2069 {
2070 if (*we == separator)
2071 {
2072 out->push_back(ImGuiTextRange(wb, we));
2073 wb = we + 1;
2074 }
2075 we++;
2076 }
2077 if (wb != we)
2078 out->push_back(ImGuiTextRange(wb, we));
2079}
2080
2081void ImGuiTextFilter::Build()
2082{
2083 Filters.resize(0);
2084 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2085 input_range.split(',', &Filters);
2086
2087 CountGrep = 0;
2088 for (int i = 0; i != Filters.Size; i++)
2089 {
2090 ImGuiTextRange& f = Filters[i];
2091 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2092 f.b++;
2093 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2094 f.e--;
2095 if (f.empty())
2096 continue;
2097 if (Filters[i].b[0] != '-')
2098 CountGrep += 1;
2099 }
2100}
2101
2102bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2103{
2104 if (Filters.empty())
2105 return true;
2106
2107 if (text == NULL)
2108 text = "";
2109
2110 for (int i = 0; i != Filters.Size; i++)
2111 {
2112 const ImGuiTextRange& f = Filters[i];
2113 if (f.empty())
2114 continue;
2115 if (f.b[0] == '-')
2116 {
2117 // Subtract
2118 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2119 return false;
2120 }
2121 else
2122 {
2123 // Grep
2124 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2125 return true;
2126 }
2127 }
2128
2129 // Implicit * grep
2130 if (CountGrep == 0)
2131 return true;
2132
2133 return false;
2134}
2135
2136//-----------------------------------------------------------------------------
2137// [SECTION] ImGuiTextBuffer
2138//-----------------------------------------------------------------------------
2139
2140// On some platform vsnprintf() takes va_list by reference and modifies it.
2141// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2142#ifndef va_copy
2143#if defined(__GNUC__) || defined(__clang__)
2144#define va_copy(dest, src) __builtin_va_copy(dest, src)
2145#else
2146#define va_copy(dest, src) (dest = src)
2147#endif
2148#endif
2149
2150char ImGuiTextBuffer::EmptyString[1] = { 0 };
2151
2152void ImGuiTextBuffer::append(const char* str, const char* str_end)
2153{
2154 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2155
2156 // Add zero-terminator the first time
2157 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2158 const int needed_sz = write_off + len;
2159 if (write_off + len >= Buf.Capacity)
2160 {
2161 int new_capacity = Buf.Capacity * 2;
2162 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2163 }
2164
2165 Buf.resize(needed_sz);
2166 memcpy(&Buf[write_off - 1], str, (size_t)len);
2167 Buf[write_off - 1 + len] = 0;
2168}
2169
2170void ImGuiTextBuffer::appendf(const char* fmt, ...)
2171{
2172 va_list args;
2173 va_start(args, fmt);
2174 appendfv(fmt, args);
2175 va_end(args);
2176}
2177
2178// Helper: Text buffer for logging/accumulating text
2179void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2180{
2181 va_list args_copy;
2182 va_copy(args_copy, args);
2183
2184 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2185 if (len <= 0)
2186 {
2187 va_end(args_copy);
2188 return;
2189 }
2190
2191 // Add zero-terminator the first time
2192 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2193 const int needed_sz = write_off + len;
2194 if (write_off + len >= Buf.Capacity)
2195 {
2196 int new_capacity = Buf.Capacity * 2;
2197 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2198 }
2199
2200 Buf.resize(needed_sz);
2201 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2202 va_end(args_copy);
2203}
2204
2205//-----------------------------------------------------------------------------
2206// [SECTION] ImGuiListClipper
2207// This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2208// the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2209//-----------------------------------------------------------------------------
2210
2211// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
2212// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
2213static bool GetSkipItemForListClipping()
2214{
2215 ImGuiContext& g = *GImGui;
2216 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
2217}
2218
2219// Helper to calculate coarse clipping of large list of evenly sized items.
2220// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2221// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
2222void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2223{
2224 ImGuiContext& g = *GImGui;
2225 ImGuiWindow* window = g.CurrentWindow;
2226 if (g.LogEnabled)
2227 {
2228 // If logging is active, do not perform any clipping
2229 *out_items_display_start = 0;
2230 *out_items_display_end = items_count;
2231 return;
2232 }
2233 if (GetSkipItemForListClipping())
2234 {
2235 *out_items_display_start = *out_items_display_end = 0;
2236 return;
2237 }
2238
2239 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2240 ImRect unclipped_rect = window->ClipRect;
2241 if (g.NavMoveRequest)
2242 unclipped_rect.Add(g.NavScoringRect);
2243 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2244 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2245
2246 const ImVec2 pos = window->DC.CursorPos;
2247 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2248 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2249
2250 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2251 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2252 start--;
2253 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2254 end++;
2255
2256 start = ImClamp(start, 0, items_count);
2257 end = ImClamp(end + 1, start, items_count);
2258 *out_items_display_start = start;
2259 *out_items_display_end = end;
2260}
2261
2262static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2263{
2264 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2265 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2266 // The clipper should probably have a 4th step to display the last item in a regular manner.
2267 ImGuiContext& g = *GImGui;
2268 ImGuiWindow* window = g.CurrentWindow;
2269 float off_y = pos_y - window->DC.CursorPos.y;
2270 window->DC.CursorPos.y = pos_y;
2271 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2272 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.
2273 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.
2274 if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
2275 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2276 if (ImGuiTable* table = g.CurrentTable)
2277 {
2278 if (table->IsInsideRow)
2279 ImGui::TableEndRow(table);
2280 table->RowPosY2 = window->DC.CursorPos.y;
2281 const int row_increase = (int)((off_y / line_height) + 0.5f);
2282 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()
2283 table->RowBgColorCounter += row_increase;
2284 }
2285}
2286
2287ImGuiListClipper::ImGuiListClipper()
2288{
2289 memset(this, 0, sizeof(*this));
2290 ItemsCount = -1;
2291}
2292
2293ImGuiListClipper::~ImGuiListClipper()
2294{
2295 IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
2296}
2297
2298// Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
2299// Use case B: Begin() called from constructor with items_height>0
2300// 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.
2301void ImGuiListClipper::Begin(int items_count, float items_height)
2302{
2303 ImGuiContext& g = *GImGui;
2304 ImGuiWindow* window = g.CurrentWindow;
2305
2306 if (ImGuiTable* table = g.CurrentTable)
2307 if (table->IsInsideRow)
2308 ImGui::TableEndRow(table);
2309
2310 StartPosY = window->DC.CursorPos.y;
2311 ItemsHeight = items_height;
2312 ItemsCount = items_count;
2313 ItemsFrozen = 0;
2314 StepNo = 0;
2315 DisplayStart = -1;
2316 DisplayEnd = 0;
2317}
2318
2319void ImGuiListClipper::End()
2320{
2321 if (ItemsCount < 0) // Already ended
2322 return;
2323
2324 // 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.
2325 if (ItemsCount < INT_MAX && DisplayStart >= 0)
2326 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
2327 ItemsCount = -1;
2328 StepNo = 3;
2329}
2330
2331bool ImGuiListClipper::Step()
2332{
2333 ImGuiContext& g = *GImGui;
2334 ImGuiWindow* window = g.CurrentWindow;
2335
2336 ImGuiTable* table = g.CurrentTable;
2337 if (table && table->IsInsideRow)
2338 ImGui::TableEndRow(table);
2339
2340 // No items
2341 if (ItemsCount == 0 || GetSkipItemForListClipping())
2342 {
2343 End();
2344 return false;
2345 }
2346
2347 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
2348 if (StepNo == 0)
2349 {
2350 // While we are in frozen row state, keep displaying items one by one, unclipped
2351 // FIXME: Could be stored as a table-agnostic state.
2352 if (table != NULL && !table->IsUnfrozenRows)
2353 {
2354 DisplayStart = ItemsFrozen;
2355 DisplayEnd = ItemsFrozen + 1;
2356 ItemsFrozen++;
2357 return true;
2358 }
2359
2360 StartPosY = window->DC.CursorPos.y;
2361 if (ItemsHeight <= 0.0f)
2362 {
2363 // Submit the first item so we can measure its height (generally it is 0..1)
2364 DisplayStart = ItemsFrozen;
2365 DisplayEnd = ItemsFrozen + 1;
2366 StepNo = 1;
2367 return true;
2368 }
2369
2370 // Already has item height (given by user in Begin): skip to calculating step
2371 DisplayStart = DisplayEnd;
2372 StepNo = 2;
2373 }
2374
2375 // Step 1: the clipper infer height from first element
2376 if (StepNo == 1)
2377 {
2378 IM_ASSERT(ItemsHeight <= 0.0f);
2379 if (table)
2380 {
2381 const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row
2382 const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell.
2383 ItemsHeight = pos_y2 - pos_y1;
2384 window->DC.CursorPos.y = pos_y2;
2385 }
2386 else
2387 {
2388 ItemsHeight = window->DC.CursorPos.y - StartPosY;
2389 }
2390 IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
2391 StepNo = 2;
2392 }
2393
2394 // Reached end of list
2395 if (DisplayEnd >= ItemsCount)
2396 {
2397 End();
2398 return false;
2399 }
2400
2401 // Step 2: calculate the actual range of elements to display, and position the cursor before the first element
2402 if (StepNo == 2)
2403 {
2404 IM_ASSERT(ItemsHeight > 0.0f);
2405
2406 int already_submitted = DisplayEnd;
2407 ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
2408 DisplayStart += already_submitted;
2409 DisplayEnd += already_submitted;
2410
2411 // Seek cursor
2412 if (DisplayStart > already_submitted)
2413 SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
2414
2415 StepNo = 3;
2416 return true;
2417 }
2418
2419 // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
2420 // Advance the cursor to the end of the list and then returns 'false' to end the loop.
2421 if (StepNo == 3)
2422 {
2423 // Seek cursor
2424 if (ItemsCount < INT_MAX)
2425 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
2426 ItemsCount = -1;
2427 return false;
2428 }
2429
2430 IM_ASSERT(0);
2431 return false;
2432}
2433
2434//-----------------------------------------------------------------------------
2435// [SECTION] STYLING
2436//-----------------------------------------------------------------------------
2437
2438ImGuiStyle& ImGui::GetStyle()
2439{
2440 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2441 return GImGui->Style;
2442}
2443
2444ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2445{
2446 ImGuiStyle& style = GImGui->Style;
2447 ImVec4 c = style.Colors[idx];
2448 c.w *= style.Alpha * alpha_mul;
2449 return ColorConvertFloat4ToU32(c);
2450}
2451
2452ImU32 ImGui::GetColorU32(const ImVec4& col)
2453{
2454 ImGuiStyle& style = GImGui->Style;
2455 ImVec4 c = col;
2456 c.w *= style.Alpha;
2457 return ColorConvertFloat4ToU32(c);
2458}
2459
2460const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2461{
2462 ImGuiStyle& style = GImGui->Style;
2463 return style.Colors[idx];
2464}
2465
2466ImU32 ImGui::GetColorU32(ImU32 col)
2467{
2468 ImGuiStyle& style = GImGui->Style;
2469 if (style.Alpha >= 1.0f)
2470 return col;
2471 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2472 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2473 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2474}
2475
2476// 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
2477void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2478{
2479 ImGuiContext& g = *GImGui;
2480 ImGuiColorMod backup;
2481 backup.Col = idx;
2482 backup.BackupValue = g.Style.Colors[idx];
2483 g.ColorStack.push_back(backup);
2484 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2485}
2486
2487void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2488{
2489 ImGuiContext& g = *GImGui;
2490 ImGuiColorMod backup;
2491 backup.Col = idx;
2492 backup.BackupValue = g.Style.Colors[idx];
2493 g.ColorStack.push_back(backup);
2494 g.Style.Colors[idx] = col;
2495}
2496
2497void ImGui::PopStyleColor(int count)
2498{
2499 ImGuiContext& g = *GImGui;
2500 while (count > 0)
2501 {
2502 ImGuiColorMod& backup = g.ColorStack.back();
2503 g.Style.Colors[backup.Col] = backup.BackupValue;
2504 g.ColorStack.pop_back();
2505 count--;
2506 }
2507}
2508
2509struct ImGuiStyleVarInfo
2510{
2511 ImGuiDataType Type;
2512 ImU32 Count;
2513 ImU32 Offset;
2514 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2515};
2516
2517static const ImGuiStyleVarInfo GStyleVarInfo[] =
2518{
2519 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
2520 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
2521 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
2522 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
2523 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
2524 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
2525 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
2526 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
2527 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
2528 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
2529 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
2530 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
2531 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
2532 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
2533 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
2534 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
2535 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding
2536 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
2537 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
2538 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
2539 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
2540 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
2541 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
2542 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2543};
2544
2545static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2546{
2547 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2548 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2549 return &GStyleVarInfo[idx];
2550}
2551
2552void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2553{
2554 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2555 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2556 {
2557 ImGuiContext& g = *GImGui;
2558 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2559 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2560 *pvar = val;
2561 return;
2562 }
2563 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2564}
2565
2566void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2567{
2568 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2569 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2570 {
2571 ImGuiContext& g = *GImGui;
2572 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2573 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
2574 *pvar = val;
2575 return;
2576 }
2577 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2578}
2579
2580void ImGui::PopStyleVar(int count)
2581{
2582 ImGuiContext& g = *GImGui;
2583 while (count > 0)
2584 {
2585 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2586 ImGuiStyleMod& backup = g.StyleVarStack.back();
2587 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2588 void* data = info->GetVarPtr(&g.Style);
2589 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
2590 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2591 g.StyleVarStack.pop_back();
2592 count--;
2593 }
2594}
2595
2596const char* ImGui::GetStyleColorName(ImGuiCol idx)
2597{
2598 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2599 switch (idx)
2600 {
2601 case ImGuiCol_Text: return "Text";
2602 case ImGuiCol_TextDisabled: return "TextDisabled";
2603 case ImGuiCol_WindowBg: return "WindowBg";
2604 case ImGuiCol_ChildBg: return "ChildBg";
2605 case ImGuiCol_PopupBg: return "PopupBg";
2606 case ImGuiCol_Border: return "Border";
2607 case ImGuiCol_BorderShadow: return "BorderShadow";
2608 case ImGuiCol_FrameBg: return "FrameBg";
2609 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2610 case ImGuiCol_FrameBgActive: return "FrameBgActive";
2611 case ImGuiCol_TitleBg: return "TitleBg";
2612 case ImGuiCol_TitleBgActive: return "TitleBgActive";
2613 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2614 case ImGuiCol_MenuBarBg: return "MenuBarBg";
2615 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2616 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2617 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2618 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2619 case ImGuiCol_CheckMark: return "CheckMark";
2620 case ImGuiCol_SliderGrab: return "SliderGrab";
2621 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2622 case ImGuiCol_Button: return "Button";
2623 case ImGuiCol_ButtonHovered: return "ButtonHovered";
2624 case ImGuiCol_ButtonActive: return "ButtonActive";
2625 case ImGuiCol_Header: return "Header";
2626 case ImGuiCol_HeaderHovered: return "HeaderHovered";
2627 case ImGuiCol_HeaderActive: return "HeaderActive";
2628 case ImGuiCol_Separator: return "Separator";
2629 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2630 case ImGuiCol_SeparatorActive: return "SeparatorActive";
2631 case ImGuiCol_ResizeGrip: return "ResizeGrip";
2632 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2633 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2634 case ImGuiCol_Tab: return "Tab";
2635 case ImGuiCol_TabHovered: return "TabHovered";
2636 case ImGuiCol_TabActive: return "TabActive";
2637 case ImGuiCol_TabUnfocused: return "TabUnfocused";
2638 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2639 case ImGuiCol_PlotLines: return "PlotLines";
2640 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2641 case ImGuiCol_PlotHistogram: return "PlotHistogram";
2642 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2643 case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
2644 case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
2645 case ImGuiCol_TableBorderLight: return "TableBorderLight";
2646 case ImGuiCol_TableRowBg: return "TableRowBg";
2647 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
2648 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2649 case ImGuiCol_DragDropTarget: return "DragDropTarget";
2650 case ImGuiCol_NavHighlight: return "NavHighlight";
2651 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2652 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2653 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2654 }
2655 IM_ASSERT(0);
2656 return "Unknown";
2657}
2658
2659
2660//-----------------------------------------------------------------------------
2661// [SECTION] RENDER HELPERS
2662// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2663// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2664// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2665//-----------------------------------------------------------------------------
2666
2667const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2668{
2669 const char* text_display_end = text;
2670 if (!text_end)
2671 text_end = (const char*)-1;
2672
2673 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2674 text_display_end++;
2675 return text_display_end;
2676}
2677
2678// Internal ImGui functions to render text
2679// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
2680void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2681{
2682 ImGuiContext& g = *GImGui;
2683 ImGuiWindow* window = g.CurrentWindow;
2684
2685 // Hide anything after a '##' string
2686 const char* text_display_end;
2687 if (hide_text_after_hash)
2688 {
2689 text_display_end = FindRenderedTextEnd(text, text_end);
2690 }
2691 else
2692 {
2693 if (!text_end)
2694 text_end = text + strlen(text); // FIXME-OPT
2695 text_display_end = text_end;
2696 }
2697
2698 if (text != text_display_end)
2699 {
2700 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2701 if (g.LogEnabled)
2702 LogRenderedText(&pos, text, text_display_end);
2703 }
2704}
2705
2706void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2707{
2708 ImGuiContext& g = *GImGui;
2709 ImGuiWindow* window = g.CurrentWindow;
2710
2711 if (!text_end)
2712 text_end = text + strlen(text); // FIXME-OPT
2713
2714 if (text != text_end)
2715 {
2716 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2717 if (g.LogEnabled)
2718 LogRenderedText(&pos, text, text_end);
2719 }
2720}
2721
2722// Default clip_rect uses (pos_min,pos_max)
2723// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
2724void 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)
2725{
2726 // Perform CPU side clipping for single clipped element to avoid using scissor state
2727 ImVec2 pos = pos_min;
2728 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2729
2730 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2731 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2732 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2733 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2734 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2735
2736 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2737 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2738 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2739
2740 // Render
2741 if (need_clipping)
2742 {
2743 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2744 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2745 }
2746 else
2747 {
2748 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2749 }
2750}
2751
2752void 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)
2753{
2754 // Hide anything after a '##' string
2755 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2756 const int text_len = (int)(text_display_end - text);
2757 if (text_len == 0)
2758 return;
2759
2760 ImGuiContext& g = *GImGui;
2761 ImGuiWindow* window = g.CurrentWindow;
2762 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2763 if (g.LogEnabled)
2764 LogRenderedText(&pos_min, text, text_display_end);
2765}
2766
2767
2768// Another overly complex function until we reorganize everything into a nice all-in-one helper.
2769// 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.
2770// 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.
2771void 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)
2772{
2773 ImGuiContext& g = *GImGui;
2774 if (text_end_full == NULL)
2775 text_end_full = FindRenderedTextEnd(text);
2776 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2777
2778 //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));
2779 //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));
2780 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2781 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2782 if (text_size.x > pos_max.x - pos_min.x)
2783 {
2784 // Hello wo...
2785 // | | |
2786 // min max ellipsis_max
2787 // <-> this is generally some padding value
2788
2789 const ImFont* font = draw_list->_Data->Font;
2790 const float font_size = draw_list->_Data->FontSize;
2791 const char* text_end_ellipsis = NULL;
2792
2793 ImWchar ellipsis_char = font->EllipsisChar;
2794 int ellipsis_char_count = 1;
2795 if (ellipsis_char == (ImWchar)-1)
2796 {
2797 ellipsis_char = font->DotChar;
2798 ellipsis_char_count = 3;
2799 }
2800 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2801
2802 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2803 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2804
2805 if (ellipsis_char_count > 1)
2806 {
2807 // Full ellipsis size without free spacing after it.
2808 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2809 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2810 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2811 }
2812
2813 // We can now claim the space between pos_max.x and ellipsis_max.x
2814 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2815 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2816 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2817 {
2818 // Always display at least 1 character if there's no room for character + ellipsis
2819 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2820 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2821 }
2822 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2823 {
2824 // 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)
2825 text_end_ellipsis--;
2826 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
2827 }
2828
2829 // Render text, render ellipsis
2830 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2831 float ellipsis_x = pos_min.x + text_size_clipped_x;
2832 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2833 for (int i = 0; i < ellipsis_char_count; i++)
2834 {
2835 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2836 ellipsis_x += ellipsis_glyph_width;
2837 }
2838 }
2839 else
2840 {
2841 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2842 }
2843
2844 if (g.LogEnabled)
2845 LogRenderedText(&pos_min, text, text_end_full);
2846}
2847
2848// Render a rectangle shaped with optional rounding and borders
2849void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2850{
2851 ImGuiContext& g = *GImGui;
2852 ImGuiWindow* window = g.CurrentWindow;
2853 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2854 const float border_size = g.Style.FrameBorderSize;
2855 if (border && border_size > 0.0f)
2856 {
2857 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2858 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2859 }
2860}
2861
2862void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2863{
2864 ImGuiContext& g = *GImGui;
2865 ImGuiWindow* window = g.CurrentWindow;
2866 const float border_size = g.Style.FrameBorderSize;
2867 if (border_size > 0.0f)
2868 {
2869 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
2870 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
2871 }
2872}
2873
2874void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2875{
2876 ImGuiContext& g = *GImGui;
2877 if (id != g.NavId)
2878 return;
2879 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2880 return;
2881 ImGuiWindow* window = g.CurrentWindow;
2882 if (window->DC.NavHideHighlightOneFrame)
2883 return;
2884
2885 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2886 ImRect display_rect = bb;
2887 display_rect.ClipWith(window->ClipRect);
2888 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2889 {
2890 const float THICKNESS = 2.0f;
2891 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2892 display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2893 bool fully_visible = window->ClipRect.Contains(display_rect);
2894 if (!fully_visible)
2895 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2896 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);
2897 if (!fully_visible)
2898 window->DrawList->PopClipRect();
2899 }
2900 if (flags & ImGuiNavHighlightFlags_TypeThin)
2901 {
2902 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f);
2903 }
2904}
2905
2906//-----------------------------------------------------------------------------
2907// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2908//-----------------------------------------------------------------------------
2909
2910// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
2911ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL)
2912{
2913 memset(this, 0, sizeof(*this));
2914 Name = ImStrdup(name);
2915 NameBufLen = (int)strlen(name) + 1;
2916 ID = ImHashStr(name);
2917 IDStack.push_back(ID);
2918 MoveId = GetID("#MOVE");
2919 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2920 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2921 AutoFitFramesX = AutoFitFramesY = -1;
2922 AutoPosLastDirection = ImGuiDir_None;
2923 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2924 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2925 LastFrameActive = -1;
2926 LastTimeActive = -1.0f;
2927 FontWindowScale = 1.0f;
2928 SettingsOffset = -1;
2929 DrawList = &DrawListInst;
2930 DrawList->_Data = &context->DrawListSharedData;
2931 DrawList->_OwnerName = Name;
2932}
2933
2934ImGuiWindow::~ImGuiWindow()
2935{
2936 IM_ASSERT(DrawList == &DrawListInst);
2937 IM_DELETE(Name);
2938 ColumnsStorage.clear_destruct();
2939}
2940
2941ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2942{
2943 ImGuiID seed = IDStack.back();
2944 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2945 ImGui::KeepAliveID(id);
2946#ifdef IMGUI_ENABLE_TEST_ENGINE
2947 ImGuiContext& g = *GImGui;
2948 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2949#endif
2950 return id;
2951}
2952
2953ImGuiID ImGuiWindow::GetID(const void* ptr)
2954{
2955 ImGuiID seed = IDStack.back();
2956 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2957 ImGui::KeepAliveID(id);
2958#ifdef IMGUI_ENABLE_TEST_ENGINE
2959 ImGuiContext& g = *GImGui;
2960 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2961#endif
2962 return id;
2963}
2964
2965ImGuiID ImGuiWindow::GetID(int n)
2966{
2967 ImGuiID seed = IDStack.back();
2968 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2969 ImGui::KeepAliveID(id);
2970#ifdef IMGUI_ENABLE_TEST_ENGINE
2971 ImGuiContext& g = *GImGui;
2972 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2973#endif
2974 return id;
2975}
2976
2977ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2978{
2979 ImGuiID seed = IDStack.back();
2980 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2981#ifdef IMGUI_ENABLE_TEST_ENGINE
2982 ImGuiContext& g = *GImGui;
2983 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2984#endif
2985 return id;
2986}
2987
2988ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2989{
2990 ImGuiID seed = IDStack.back();
2991 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2992#ifdef IMGUI_ENABLE_TEST_ENGINE
2993 ImGuiContext& g = *GImGui;
2994 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2995#endif
2996 return id;
2997}
2998
2999ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
3000{
3001 ImGuiID seed = IDStack.back();
3002 ImGuiID id = ImHashData(&n, sizeof(n), seed);
3003#ifdef IMGUI_ENABLE_TEST_ENGINE
3004 ImGuiContext& g = *GImGui;
3005 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
3006#endif
3007 return id;
3008}
3009
3010// This is only used in rare/specific situations to manufacture an ID out of nowhere.
3011ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
3012{
3013 ImGuiID seed = IDStack.back();
3014 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) };
3015 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
3016 ImGui::KeepAliveID(id);
3017 return id;
3018}
3019
3020static void SetCurrentWindow(ImGuiWindow* window)
3021{
3022 ImGuiContext& g = *GImGui;
3023 g.CurrentWindow = window;
3024 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
3025 if (window)
3026 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
3027}
3028
3029void ImGui::GcCompactTransientMiscBuffers()
3030{
3031 ImGuiContext& g = *GImGui;
3032 g.ItemFlagsStack.clear();
3033 g.GroupStack.clear();
3034 TableGcCompactSettings();
3035}
3036
3037// Free up/compact internal window buffers, we can use this when a window becomes unused.
3038// Not freed:
3039// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
3040// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
3041void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
3042{
3043 window->MemoryCompacted = true;
3044 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
3045 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
3046 window->IDStack.clear();
3047 window->DrawList->_ClearFreeMemory();
3048 window->DC.ChildWindows.clear();
3049 window->DC.ItemWidthStack.clear();
3050 window->DC.TextWrapPosStack.clear();
3051}
3052
3053void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
3054{
3055 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
3056 // The other buffers tends to amortize much faster.
3057 window->MemoryCompacted = false;
3058 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
3059 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
3060 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
3061}
3062
3063void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
3064{
3065 ImGuiContext& g = *GImGui;
3066 g.ActiveIdIsJustActivated = (g.ActiveId != id);
3067 if (g.ActiveIdIsJustActivated)
3068 {
3069 g.ActiveIdTimer = 0.0f;
3070 g.ActiveIdHasBeenPressedBefore = false;
3071 g.ActiveIdHasBeenEditedBefore = false;
3072 g.ActiveIdMouseButton = -1;
3073 if (id != 0)
3074 {
3075 g.LastActiveId = id;
3076 g.LastActiveIdTimer = 0.0f;
3077 }
3078 }
3079 g.ActiveId = id;
3080 g.ActiveIdAllowOverlap = false;
3081 g.ActiveIdNoClearOnFocusLoss = false;
3082 g.ActiveIdWindow = window;
3083 g.ActiveIdHasBeenEditedThisFrame = false;
3084 if (id)
3085 {
3086 g.ActiveIdIsAlive = id;
3087 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
3088 }
3089
3090 // Clear declaration of inputs claimed by the widget
3091 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
3092 g.ActiveIdUsingMouseWheel = false;
3093 g.ActiveIdUsingNavDirMask = 0x00;
3094 g.ActiveIdUsingNavInputMask = 0x00;
3095 g.ActiveIdUsingKeyInputMask = 0x00;
3096}
3097
3098void ImGui::ClearActiveID()
3099{
3100 SetActiveID(0, NULL); // g.ActiveId = 0;
3101}
3102
3103void ImGui::SetHoveredID(ImGuiID id)
3104{
3105 ImGuiContext& g = *GImGui;
3106 g.HoveredId = id;
3107 g.HoveredIdAllowOverlap = false;
3108 g.HoveredIdUsingMouseWheel = false;
3109 if (id != 0 && g.HoveredIdPreviousFrame != id)
3110 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
3111}
3112
3113ImGuiID ImGui::GetHoveredID()
3114{
3115 ImGuiContext& g = *GImGui;
3116 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
3117}
3118
3119void ImGui::KeepAliveID(ImGuiID id)
3120{
3121 ImGuiContext& g = *GImGui;
3122 if (g.ActiveId == id)
3123 g.ActiveIdIsAlive = id;
3124 if (g.ActiveIdPreviousFrame == id)
3125 g.ActiveIdPreviousFrameIsAlive = true;
3126}
3127
3128void ImGui::MarkItemEdited(ImGuiID id)
3129{
3130 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
3131 // 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.
3132 ImGuiContext& g = *GImGui;
3133 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
3134 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
3135 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
3136 g.ActiveIdHasBeenEditedThisFrame = true;
3137 g.ActiveIdHasBeenEditedBefore = true;
3138 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
3139}
3140
3141static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3142{
3143 // An active popup disable hovering on other windows (apart from its own children)
3144 // FIXME-OPT: This could be cached/stored within the window.
3145 ImGuiContext& g = *GImGui;
3146 if (g.NavWindow)
3147 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3148 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3149 {
3150 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3151 // NB: The order of those two tests is important because Modal windows are also Popups.
3152 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3153 return false;
3154 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3155 return false;
3156 }
3157 return true;
3158}
3159
3160// This is roughly matching the behavior of internal-facing ItemHoverable()
3161// - 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()
3162// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
3163bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3164{
3165 ImGuiContext& g = *GImGui;
3166 ImGuiWindow* window = g.CurrentWindow;
3167 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3168 {
3169 if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3170 return false;
3171 return IsItemFocused();
3172 }
3173
3174 // Test for bounding box overlap, as updated as ItemAdd()
3175 ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
3176 if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
3177 return false;
3178 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
3179
3180 // Test if we are hovering the right window (our window could be behind another window)
3181 // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
3182 // [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
3183 // 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
3184 // the test that has been running for a long while.
3185 if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
3186 if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0)
3187 return false;
3188
3189 // Test if another item is active (e.g. being dragged)
3190 if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
3191 if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3192 return false;
3193
3194 // Test if interactions on this window are blocked by an active popup or modal.
3195 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3196 if (!IsWindowContentHoverable(window, flags))
3197 return false;
3198
3199 // Test if the item is disabled
3200 if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3201 return false;
3202
3203 // Special handling for calling after Begin() which represent the title bar or tab.
3204 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3205 if (g.LastItemData.ID == window->MoveId && window->WriteAccessed)
3206 return false;
3207 return true;
3208}
3209
3210// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
3211bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3212{
3213 ImGuiContext& g = *GImGui;
3214 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3215 return false;
3216
3217 ImGuiWindow* window = g.CurrentWindow;
3218 if (g.HoveredWindow != window)
3219 return false;
3220 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3221 return false;
3222 if (!IsMouseHoveringRect(bb.Min, bb.Max))
3223 return false;
3224 if (g.NavDisableMouseHover)
3225 return false;
3226 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
3227 {
3228 g.HoveredIdDisabled = true;
3229 return false;
3230 }
3231
3232 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3233 // hover test in widgets code. We could also decide to split this function is two.
3234 if (id != 0)
3235 SetHoveredID(id);
3236
3237 // When disabled we'll return false but still set HoveredId
3238 ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);
3239 if (item_flags & ImGuiItemFlags_Disabled)
3240 {
3241 // Release active id if turning disabled
3242 if (g.ActiveId == id)
3243 ClearActiveID();
3244 g.HoveredIdDisabled = true;
3245 return false;
3246 }
3247
3248 if (id != 0)
3249 {
3250 // [DEBUG] Item Picker tool!
3251 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3252 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3253 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3254 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3255 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3256 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3257 if (g.DebugItemPickerBreakId == id)
3258 IM_DEBUG_BREAK();
3259 }
3260
3261 return true;
3262}
3263
3264bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3265{
3266 ImGuiContext& g = *GImGui;
3267 ImGuiWindow* window = g.CurrentWindow;
3268 if (!bb.Overlaps(window->ClipRect))
3269 if (id == 0 || (id != g.ActiveId && id != g.NavId))
3270 if (clip_even_when_logged || !g.LogEnabled)
3271 return true;
3272 return false;
3273}
3274
3275// Called by ItemAdd()
3276// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
3277void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id)
3278{
3279 ImGuiContext& g = *GImGui;
3280 IM_ASSERT(id != 0 && id == g.LastItemData.ID);
3281
3282 // Increment counters
3283 // FIXME: ImGuiItemFlags_Disabled should disable more.
3284 const bool is_tab_stop = (g.LastItemData.InFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3285 window->DC.FocusCounterRegular++;
3286 if (is_tab_stop)
3287 {
3288 window->DC.FocusCounterTabStop++;
3289 if (g.NavId == id)
3290 g.NavIdTabCounter = window->DC.FocusCounterTabStop;
3291 }
3292
3293 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3294 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3295 if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL)
3296 {
3297 g.TabFocusRequestNextWindow = window;
3298 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.
3299 }
3300
3301 // Handle focus requests
3302 if (g.TabFocusRequestCurrWindow == window)
3303 {
3304 if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular)
3305 {
3306 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByCode;
3307 return;
3308 }
3309 if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop)
3310 {
3311 g.NavJustTabbedId = id;
3312 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing;
3313 return;
3314 }
3315
3316 // If another item is about to be focused, we clear our own active id
3317 if (g.ActiveId == id)
3318 ClearActiveID();
3319 }
3320}
3321
3322float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3323{
3324 if (wrap_pos_x < 0.0f)
3325 return 0.0f;
3326
3327 ImGuiContext& g = *GImGui;
3328 ImGuiWindow* window = g.CurrentWindow;
3329 if (wrap_pos_x == 0.0f)
3330 {
3331 // We could decide to setup a default wrapping max point for auto-resizing windows,
3332 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3333 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3334 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3335 //else
3336 wrap_pos_x = window->WorkRect.Max.x;
3337 }
3338 else if (wrap_pos_x > 0.0f)
3339 {
3340 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3341 }
3342
3343 return ImMax(wrap_pos_x - pos.x, 1.0f);
3344}
3345
3346// IM_ALLOC() == ImGui::MemAlloc()
3347void* ImGui::MemAlloc(size_t size)
3348{
3349 if (ImGuiContext* ctx = GImGui)
3350 ctx->IO.MetricsActiveAllocations++;
3351 return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
3352}
3353
3354// IM_FREE() == ImGui::MemFree()
3355void ImGui::MemFree(void* ptr)
3356{
3357 if (ptr)
3358 if (ImGuiContext* ctx = GImGui)
3359 ctx->IO.MetricsActiveAllocations--;
3360 return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
3361}
3362
3363const char* ImGui::GetClipboardText()
3364{
3365 ImGuiContext& g = *GImGui;
3366 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3367}
3368
3369void ImGui::SetClipboardText(const char* text)
3370{
3371 ImGuiContext& g = *GImGui;
3372 if (g.IO.SetClipboardTextFn)
3373 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3374}
3375
3376const char* ImGui::GetVersion()
3377{
3378 return IMGUI_VERSION;
3379}
3380
3381// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3382// 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
3383ImGuiContext* ImGui::GetCurrentContext()
3384{
3385 return GImGui;
3386}
3387
3388void ImGui::SetCurrentContext(ImGuiContext* ctx)
3389{
3390#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3391 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3392#else
3393 GImGui = ctx;
3394#endif
3395}
3396
3397void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3398{
3399 GImAllocatorAllocFunc = alloc_func;
3400 GImAllocatorFreeFunc = free_func;
3401 GImAllocatorUserData = user_data;
3402}
3403
3404// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
3405void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3406{
3407 *p_alloc_func = GImAllocatorAllocFunc;
3408 *p_free_func = GImAllocatorFreeFunc;
3409 *p_user_data = GImAllocatorUserData;
3410}
3411
3412ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3413{
3414 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3415 if (GImGui == NULL)
3416 SetCurrentContext(ctx);
3417 Initialize(ctx);
3418 return ctx;
3419}
3420
3421void ImGui::DestroyContext(ImGuiContext* ctx)
3422{
3423 if (ctx == NULL)
3424 ctx = GImGui;
3425 Shutdown(ctx);
3426 if (GImGui == ctx)
3427 SetCurrentContext(NULL);
3428 IM_DELETE(ctx);
3429}
3430
3431// No specific ordering/dependency support, will see as needed
3432ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
3433{
3434 ImGuiContext& g = *ctx;
3435 IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
3436 g.Hooks.push_back(*hook);
3437 g.Hooks.back().HookId = ++g.HookIdNext;
3438 return g.HookIdNext;
3439}
3440
3441// Deferred removal, avoiding issue with changing vector while iterating it
3442void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
3443{
3444 ImGuiContext& g = *ctx;
3445 IM_ASSERT(hook_id != 0);
3446 for (int n = 0; n < g.Hooks.Size; n++)
3447 if (g.Hooks[n].HookId == hook_id)
3448 g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_;
3449}
3450
3451// Call context hooks (used by e.g. test engine)
3452// We assume a small number of hooks so all stored in same array
3453void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
3454{
3455 ImGuiContext& g = *ctx;
3456 for (int n = 0; n < g.Hooks.Size; n++)
3457 if (g.Hooks[n].Type == hook_type)
3458 g.Hooks[n].Callback(&g, &g.Hooks[n]);
3459}
3460
3461ImGuiIO& ImGui::GetIO()
3462{
3463 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3464 return GImGui->IO;
3465}
3466
3467// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
3468ImDrawData* ImGui::GetDrawData()
3469{
3470 ImGuiContext& g = *GImGui;
3471 ImGuiViewportP* viewport = g.Viewports[0];
3472 return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
3473}
3474
3475double ImGui::GetTime()
3476{
3477 return GImGui->Time;
3478}
3479
3480int ImGui::GetFrameCount()
3481{
3482 return GImGui->FrameCount;
3483}
3484
3485static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
3486{
3487 // Create the draw list on demand, because they are not frequently used for all viewports
3488 ImGuiContext& g = *GImGui;
3489 IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists));
3490 ImDrawList* draw_list = viewport->DrawLists[drawlist_no];
3491 if (draw_list == NULL)
3492 {
3493 draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
3494 draw_list->_OwnerName = drawlist_name;
3495 viewport->DrawLists[drawlist_no] = draw_list;
3496 }
3497
3498 // Our ImDrawList system requires that there is always a command
3499 if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount)
3500 {
3501 draw_list->_ResetForNewFrame();
3502 draw_list->PushTextureID(g.IO.Fonts->TexID);
3503 draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
3504 viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount;
3505 }
3506 return draw_list;
3507}
3508
3509ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
3510{
3511 return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background");
3512}
3513
3514ImDrawList* ImGui::GetBackgroundDrawList()
3515{
3516 ImGuiContext& g = *GImGui;
3517 return GetBackgroundDrawList(g.Viewports[0]);
3518}
3519
3520ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
3521{
3522 return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
3523}
3524
3525ImDrawList* ImGui::GetForegroundDrawList()
3526{
3527 ImGuiContext& g = *GImGui;
3528 return GetForegroundDrawList(g.Viewports[0]);
3529}
3530
3531ImDrawListSharedData* ImGui::GetDrawListSharedData()
3532{
3533 return &GImGui->DrawListSharedData;
3534}
3535
3536void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3537{
3538 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3539 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3540 // This is because we want ActiveId to be set even when the window is not permitted to move.
3541 ImGuiContext& g = *GImGui;
3542 FocusWindow(window);
3543 SetActiveID(window->MoveId, window);
3544 g.NavDisableHighlight = true;
3545 g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
3546 g.ActiveIdNoClearOnFocusLoss = true;
3547 SetActiveIdUsingNavAndKeys();
3548
3549 bool can_move_window = true;
3550 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3551 can_move_window = false;
3552 if (can_move_window)
3553 g.MovingWindow = window;
3554}
3555
3556// Handle mouse moving window
3557// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3558// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3559// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3560// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
3561void ImGui::UpdateMouseMovingWindowNewFrame()
3562{
3563 ImGuiContext& g = *GImGui;
3564 if (g.MovingWindow != NULL)
3565 {
3566 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3567 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3568 KeepAliveID(g.ActiveId);
3569 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3570 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3571 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3572 {
3573 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3574 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3575 {
3576 MarkIniSettingsDirty(moving_window);
3577 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3578 }
3579 FocusWindow(g.MovingWindow);
3580 }
3581 else
3582 {
3583 g.MovingWindow = NULL;
3584 ClearActiveID();
3585 }
3586 }
3587 else
3588 {
3589 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3590 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3591 {
3592 KeepAliveID(g.ActiveId);
3593 if (!g.IO.MouseDown[0])
3594 ClearActiveID();
3595 }
3596 }
3597}
3598
3599// Initiate moving window when clicking on empty space or title bar.
3600// Handle left-click and right-click focus.
3601void ImGui::UpdateMouseMovingWindowEndFrame()
3602{
3603 ImGuiContext& g = *GImGui;
3604 if (g.ActiveId != 0 || g.HoveredId != 0)
3605 return;
3606
3607 // Unless we just made a window/popup appear
3608 if (g.NavWindow && g.NavWindow->Appearing)
3609 return;
3610
3611 // Click on empty space to focus window and start moving
3612 // (after we're done with all our widgets)
3613 if (g.IO.MouseClicked[0])
3614 {
3615 // Handle the edge case of a popup being closed while clicking in its empty space.
3616 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3617 ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3618 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3619
3620 if (root_window != NULL && !is_closed_popup)
3621 {
3622 StartMouseMovingWindow(g.HoveredWindow); //-V595
3623
3624 // Cancel moving if clicked outside of title bar
3625 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
3626 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3627 g.MovingWindow = NULL;
3628
3629 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3630 if (g.HoveredIdDisabled)
3631 g.MovingWindow = NULL;
3632 }
3633 else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3634 {
3635 // Clicking on void disable focus
3636 FocusWindow(NULL);
3637 }
3638 }
3639
3640 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3641 // Instead, focus will be restored to the window under the bottom-most closed popup.
3642 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3643 if (g.IO.MouseClicked[1])
3644 {
3645 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3646 // This is where we can trim the popup stack.
3647 ImGuiWindow* modal = GetTopMostPopupModal();
3648 bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal);
3649 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3650 }
3651}
3652
3653static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3654{
3655 return (window->Active) && (!window->Hidden);
3656}
3657
3658static void ImGui::UpdateMouseInputs()
3659{
3660 ImGuiContext& g = *GImGui;
3661
3662 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3663 if (IsMousePosValid(&g.IO.MousePos))
3664 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3665
3666 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3667 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3668 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3669 else
3670 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3671 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3672 g.NavDisableMouseHover = false;
3673
3674 g.IO.MousePosPrev = g.IO.MousePos;
3675 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3676 {
3677 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3678 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3679 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3680 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;
3681 g.IO.MouseDoubleClicked[i] = false;
3682 if (g.IO.MouseClicked[i])
3683 {
3684 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3685 {
3686 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3687 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3688 g.IO.MouseDoubleClicked[i] = true;
3689 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3690 }
3691 else
3692 {
3693 g.IO.MouseClickedTime[i] = g.Time;
3694 }
3695 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3696 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3697 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3698 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3699 }
3700 else if (g.IO.MouseDown[i])
3701 {
3702 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3703 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3704 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3705 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);
3706 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);
3707 }
3708 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3709 g.IO.MouseDownWasDoubleClick[i] = false;
3710 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3711 g.NavDisableMouseHover = false;
3712 }
3713}
3714
3715static void StartLockWheelingWindow(ImGuiWindow* window)
3716{
3717 ImGuiContext& g = *GImGui;
3718 if (g.WheelingWindow == window)
3719 return;
3720 g.WheelingWindow = window;
3721 g.WheelingWindowRefMousePos = g.IO.MousePos;
3722 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3723}
3724
3725void ImGui::UpdateMouseWheel()
3726{
3727 ImGuiContext& g = *GImGui;
3728
3729 // Reset the locked window if we move the mouse or after the timer elapses
3730 if (g.WheelingWindow != NULL)
3731 {
3732 g.WheelingWindowTimer -= g.IO.DeltaTime;
3733 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3734 g.WheelingWindowTimer = 0.0f;
3735 if (g.WheelingWindowTimer <= 0.0f)
3736 {
3737 g.WheelingWindow = NULL;
3738 g.WheelingWindowTimer = 0.0f;
3739 }
3740 }
3741
3742 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3743 return;
3744
3745 if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel))
3746 return;
3747
3748 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3749 if (!window || window->Collapsed)
3750 return;
3751
3752 // Zoom / Scale window
3753 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3754 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3755 {
3756 StartLockWheelingWindow(window);
3757 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3758 const float scale = new_font_scale / window->FontWindowScale;
3759 window->FontWindowScale = new_font_scale;
3760 if (window == window->RootWindow)
3761 {
3762 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3763 SetWindowPos(window, window->Pos + offset, 0);
3764 window->Size = ImFloor(window->Size * scale);
3765 window->SizeFull = ImFloor(window->SizeFull * scale);
3766 }
3767 return;
3768 }
3769
3770 // Mouse wheel scrolling
3771 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3772 if (g.IO.KeyCtrl)
3773 return;
3774
3775 // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
3776 // (we avoid doing it on OSX as it the OS input layer handles this already)
3777 const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors;
3778 const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel;
3779 const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH;
3780
3781 // Vertical Mouse Wheel scrolling
3782 if (wheel_y != 0.0f)
3783 {
3784 StartLockWheelingWindow(window);
3785 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3786 window = window->ParentWindow;
3787 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3788 {
3789 float max_step = window->InnerRect.GetHeight() * 0.67f;
3790 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3791 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3792 }
3793 }
3794
3795 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3796 if (wheel_x != 0.0f)
3797 {
3798 StartLockWheelingWindow(window);
3799 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3800 window = window->ParentWindow;
3801 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3802 {
3803 float max_step = window->InnerRect.GetWidth() * 0.67f;
3804 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3805 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3806 }
3807 }
3808}
3809
3810void ImGui::UpdateTabFocus()
3811{
3812 ImGuiContext& g = *GImGui;
3813
3814 // Pressing TAB activate widget focus
3815 g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3816 if (g.ActiveId == 0 && g.TabFocusPressed)
3817 {
3818 // - This path is only taken when no widget are active/tabbed-into yet.
3819 // Subsequent tabbing will be processed by FocusableItemRegister()
3820 // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3821 // manipulate the Next fields here even though they will be turned into Curr fields below.
3822 g.TabFocusRequestNextWindow = g.NavWindow;
3823 g.TabFocusRequestNextCounterRegular = INT_MAX;
3824 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3825 g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0);
3826 else
3827 g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3828 }
3829
3830 // Turn queued focus request into current one
3831 g.TabFocusRequestCurrWindow = NULL;
3832 g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX;
3833 if (g.TabFocusRequestNextWindow != NULL)
3834 {
3835 ImGuiWindow* window = g.TabFocusRequestNextWindow;
3836 g.TabFocusRequestCurrWindow = window;
3837 if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3838 g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3839 if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3840 g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3841 g.TabFocusRequestNextWindow = NULL;
3842 g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX;
3843 }
3844
3845 g.NavIdTabCounter = INT_MAX;
3846}
3847
3848// 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)
3849void ImGui::UpdateHoveredWindowAndCaptureFlags()
3850{
3851 ImGuiContext& g = *GImGui;
3852 g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));
3853
3854 // Find the window hovered by mouse:
3855 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3856 // - 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.
3857 // - 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.
3858 bool clear_hovered_windows = false;
3859 FindHoveredWindow();
3860
3861 // Modal windows prevents mouse from hovering behind them.
3862 ImGuiWindow* modal_window = GetTopMostPopupModal();
3863 if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window))
3864 clear_hovered_windows = true;
3865
3866 // Disabled mouse?
3867 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3868 clear_hovered_windows = true;
3869
3870 // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
3871 int mouse_earliest_button_down = -1;
3872 bool mouse_any_down = false;
3873 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3874 {
3875 if (g.IO.MouseClicked[i])
3876 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
3877 mouse_any_down |= g.IO.MouseDown[i];
3878 if (g.IO.MouseDown[i])
3879 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3880 mouse_earliest_button_down = i;
3881 }
3882 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3883
3884 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3885 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3886 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3887 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3888 clear_hovered_windows = true;
3889
3890 if (clear_hovered_windows)
3891 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
3892
3893 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3894 if (g.WantCaptureMouseNextFrame != -1)
3895 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3896 else
3897 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
3898
3899 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3900 if (g.WantCaptureKeyboardNextFrame != -1)
3901 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3902 else
3903 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3904 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3905 g.IO.WantCaptureKeyboard = true;
3906
3907 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3908 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3909}
3910
3911ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3912{
3913 ImGuiContext& g = *GImGui;
3914 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3915 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3916 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3917 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3918 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; }
3919 return key_mod_flags;
3920}
3921
3922void ImGui::NewFrame()
3923{
3924 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3925 ImGuiContext& g = *GImGui;
3926
3927 // Remove pending delete hooks before frame start.
3928 // This deferred removal avoid issues of removal while iterating the hook vector
3929 for (int n = g.Hooks.Size - 1; n >= 0; n--)
3930 if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
3931 g.Hooks.erase(&g.Hooks[n]);
3932
3933 CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
3934
3935 // Check and assert for various common IO and Configuration mistakes
3936 ErrorCheckNewFrameSanityChecks();
3937
3938 // Load settings on first frame, save settings when modified (after a delay)
3939 UpdateSettings();
3940
3941 g.Time += g.IO.DeltaTime;
3942 g.WithinFrameScope = true;
3943 g.FrameCount += 1;
3944 g.TooltipOverrideCount = 0;
3945 g.WindowsActiveCount = 0;
3946 g.MenusIdSubmittedThisFrame.resize(0);
3947
3948 // Calculate frame-rate for the user, as a purely luxurious feature
3949 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3950 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3951 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3952 g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
3953 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
3954
3955 UpdateViewportsNewFrame();
3956
3957 // Setup current font and draw list shared data
3958 g.IO.Fonts->Locked = true;
3959 SetCurrentFont(GetDefaultFont());
3960 IM_ASSERT(g.Font->IsLoaded());
3961 ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
3962 for (int n = 0; n < g.Viewports.Size; n++)
3963 virtual_space.Add(g.Viewports[n]->GetMainRect());
3964 g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
3965 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3966 g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
3967 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3968 if (g.Style.AntiAliasedLines)
3969 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3970 if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
3971 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
3972 if (g.Style.AntiAliasedFill)
3973 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3974 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3975 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3976
3977 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3978 for (int n = 0; n < g.Viewports.Size; n++)
3979 {
3980 ImGuiViewportP* viewport = g.Viewports[n];
3981 viewport->DrawDataP.Clear();
3982 }
3983
3984 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3985 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3986 KeepAliveID(g.DragDropPayload.SourceId);
3987
3988 // Update HoveredId data
3989 if (!g.HoveredIdPreviousFrame)
3990 g.HoveredIdTimer = 0.0f;
3991 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3992 g.HoveredIdNotActiveTimer = 0.0f;
3993 if (g.HoveredId)
3994 g.HoveredIdTimer += g.IO.DeltaTime;
3995 if (g.HoveredId && g.ActiveId != g.HoveredId)
3996 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3997 g.HoveredIdPreviousFrame = g.HoveredId;
3998 g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel;
3999 g.HoveredId = 0;
4000 g.HoveredIdAllowOverlap = false;
4001 g.HoveredIdUsingMouseWheel = false;
4002 g.HoveredIdDisabled = false;
4003
4004 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
4005 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
4006 ClearActiveID();
4007 if (g.ActiveId)
4008 g.ActiveIdTimer += g.IO.DeltaTime;
4009 g.LastActiveIdTimer += g.IO.DeltaTime;
4010 g.ActiveIdPreviousFrame = g.ActiveId;
4011 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
4012 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4013 g.ActiveIdIsAlive = 0;
4014 g.ActiveIdHasBeenEditedThisFrame = false;
4015 g.ActiveIdPreviousFrameIsAlive = false;
4016 g.ActiveIdIsJustActivated = false;
4017 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
4018 g.TempInputId = 0;
4019 if (g.ActiveId == 0)
4020 {
4021 g.ActiveIdUsingNavDirMask = 0x00;
4022 g.ActiveIdUsingNavInputMask = 0x00;
4023 g.ActiveIdUsingKeyInputMask = 0x00;
4024 }
4025
4026 // Drag and drop
4027 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
4028 g.DragDropAcceptIdCurr = 0;
4029 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
4030 g.DragDropWithinSource = false;
4031 g.DragDropWithinTarget = false;
4032 g.DragDropHoldJustPressedId = 0;
4033
4034 // Update keyboard input state
4035 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
4036 g.IO.KeyMods = GetMergedKeyModFlags();
4037 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
4038 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
4039 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;
4040
4041 // Update gamepad/keyboard navigation
4042 NavUpdate();
4043
4044 // Update mouse input state
4045 UpdateMouseInputs();
4046
4047 // Find hovered window
4048 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
4049 UpdateHoveredWindowAndCaptureFlags();
4050
4051 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
4052 UpdateMouseMovingWindowNewFrame();
4053
4054 // Background darkening/whitening
4055 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
4056 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
4057 else
4058 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
4059
4060 g.MouseCursor = ImGuiMouseCursor_Arrow;
4061 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
4062 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
4063
4064 // Mouse wheel scrolling, scale
4065 UpdateMouseWheel();
4066
4067 // Update legacy TAB focus
4068 UpdateTabFocus();
4069
4070 // Mark all windows as not visible and compact unused memory.
4071 IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
4072 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
4073 for (int i = 0; i != g.Windows.Size; i++)
4074 {
4075 ImGuiWindow* window = g.Windows[i];
4076 window->WasActive = window->Active;
4077 window->BeginCount = 0;
4078 window->Active = false;
4079 window->WriteAccessed = false;
4080
4081 // Garbage collect transient buffers of recently unused windows
4082 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
4083 GcCompactTransientWindowBuffers(window);
4084 }
4085
4086 // Garbage collect transient buffers of recently unused tables
4087 for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
4088 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
4089 TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
4090 for (int i = 0; i < g.TablesTempDataStack.Size; i++)
4091 if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time)
4092 TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]);
4093 if (g.GcCompactAll)
4094 GcCompactTransientMiscBuffers();
4095 g.GcCompactAll = false;
4096
4097 // Closing the focused window restore focus to the first active root window in descending z-order
4098 if (g.NavWindow && !g.NavWindow->WasActive)
4099 FocusTopMostWindowUnderOne(NULL, NULL);
4100
4101 // No window should be open at the beginning of the frame.
4102 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
4103 g.CurrentWindowStack.resize(0);
4104 g.BeginPopupStack.resize(0);
4105 g.ItemFlagsStack.resize(0);
4106 g.ItemFlagsStack.push_back(ImGuiItemFlags_None);
4107 g.GroupStack.resize(0);
4108
4109 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4110 UpdateDebugToolItemPicker();
4111
4112 // Create implicit/fallback window - which we will only render it if the user has added something to it.
4113 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
4114 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
4115 g.WithinFrameScopeWithImplicitWindow = true;
4116 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
4117 Begin("Debug##Default");
4118 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
4119
4120 CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
4121}
4122
4123// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
4124void ImGui::UpdateDebugToolItemPicker()
4125{
4126 ImGuiContext& g = *GImGui;
4127 g.DebugItemPickerBreakId = 0;
4128 if (g.DebugItemPickerActive)
4129 {
4130 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
4131 SetMouseCursor(ImGuiMouseCursor_Hand);
4132 if (IsKeyPressedMap(ImGuiKey_Escape))
4133 g.DebugItemPickerActive = false;
4134 if (IsMouseClicked(0) && hovered_id)
4135 {
4136 g.DebugItemPickerBreakId = hovered_id;
4137 g.DebugItemPickerActive = false;
4138 }
4139 SetNextWindowBgAlpha(0.60f);
4140 BeginTooltip();
4141 Text("HoveredId: 0x%08X", hovered_id);
4142 Text("Press ESC to abort picking.");
4143 TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
4144 EndTooltip();
4145 }
4146}
4147
4148void ImGui::Initialize(ImGuiContext* context)
4149{
4150 ImGuiContext& g = *context;
4151 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4152
4153 // Add .ini handle for ImGuiWindow type
4154 {
4155 ImGuiSettingsHandler ini_handler;
4156 ini_handler.TypeName = "Window";
4157 ini_handler.TypeHash = ImHashStr("Window");
4158 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4159 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4160 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4161 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4162 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4163 g.SettingsHandlers.push_back(ini_handler);
4164 }
4165
4166 // Add .ini handle for ImGuiTable type
4167 TableSettingsInstallHandler(context);
4168
4169 // Create default viewport
4170 ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4171 g.Viewports.push_back(viewport);
4172
4173#ifdef IMGUI_HAS_DOCK
4174#endif
4175
4176 g.Initialized = true;
4177}
4178
4179// This function is merely here to free heap allocations.
4180void ImGui::Shutdown(ImGuiContext* context)
4181{
4182 // 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)
4183 ImGuiContext& g = *context;
4184 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
4185 {
4186 g.IO.Fonts->Locked = false;
4187 IM_DELETE(g.IO.Fonts);
4188 }
4189 g.IO.Fonts = NULL;
4190
4191 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4192 if (!g.Initialized)
4193 return;
4194
4195 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4196 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4197 {
4198 ImGuiContext* backup_context = GImGui;
4199 SetCurrentContext(&g);
4200 SaveIniSettingsToDisk(g.IO.IniFilename);
4201 SetCurrentContext(backup_context);
4202 }
4203
4204 CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4205
4206 // Clear everything else
4207 g.Windows.clear_delete();
4208 g.WindowsFocusOrder.clear();
4209 g.WindowsTempSortBuffer.clear();
4210 g.CurrentWindow = NULL;
4211 g.CurrentWindowStack.clear();
4212 g.WindowsById.Clear();
4213 g.NavWindow = NULL;
4214 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4215 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
4216 g.MovingWindow = NULL;
4217 g.ColorStack.clear();
4218 g.StyleVarStack.clear();
4219 g.FontStack.clear();
4220 g.OpenPopupStack.clear();
4221 g.BeginPopupStack.clear();
4222
4223 g.Viewports.clear_delete();
4224
4225 g.TabBars.Clear();
4226 g.CurrentTabBarStack.clear();
4227 g.ShrinkWidthBuffer.clear();
4228
4229 g.Tables.Clear();
4230 g.TablesTempDataStack.clear_destruct();
4231 g.DrawChannelsTempMergeBuffer.clear();
4232
4233 g.ClipboardHandlerData.clear();
4234 g.MenusIdSubmittedThisFrame.clear();
4235 g.InputTextState.ClearFreeMemory();
4236
4237 g.SettingsWindows.clear();
4238 g.SettingsHandlers.clear();
4239
4240 if (g.LogFile)
4241 {
4242#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4243 if (g.LogFile != stdout)
4244#endif
4245 ImFileClose(g.LogFile);
4246 g.LogFile = NULL;
4247 }
4248 g.LogBuffer.clear();
4249
4250 g.Initialized = false;
4251}
4252
4253// FIXME: Add a more explicit sort order in the window structure.
4254static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4255{
4256 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4257 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4258 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4259 return d;
4260 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4261 return d;
4262 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4263}
4264
4265static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4266{
4267 out_sorted_windows->push_back(window);
4268 if (window->Active)
4269 {
4270 int count = window->DC.ChildWindows.Size;
4271 if (count > 1)
4272 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4273 for (int i = 0; i < count; i++)
4274 {
4275 ImGuiWindow* child = window->DC.ChildWindows[i];
4276 if (child->Active)
4277 AddWindowToSortBuffer(out_sorted_windows, child);
4278 }
4279 }
4280}
4281
4282static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4283{
4284 // Remove trailing command if unused.
4285 // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well.
4286 draw_list->_PopUnusedDrawCmd();
4287 if (draw_list->CmdBuffer.Size == 0)
4288 return;
4289
4290 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4291 // May trigger for you if you are using PrimXXX functions incorrectly.
4292 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4293 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4294 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4295 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4296
4297 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4298 // If this assert triggers because you are drawing lots of stuff manually:
4299 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4300 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
4301 // - If you want large meshes with more than 64K vertices, you can either:
4302 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4303 // Most example backends already support this from 1.71. Pre-1.71 backends won't.
4304 // 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.
4305 // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4306 // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
4307 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4308 // Your own engine or render API may use different parameters or function calls to specify index sizes.
4309 // 2 and 4 bytes indices are generally supported by most graphics API.
4310 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4311 // the 64K limit to split your draw commands in multiple draw lists.
4312 if (sizeof(ImDrawIdx) == 2)
4313 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4314
4315 out_list->push_back(draw_list);
4316}
4317
4318static void AddWindowToDrawData(ImGuiWindow* window, int layer)
4319{
4320 ImGuiContext& g = *GImGui;
4321 ImGuiViewportP* viewport = g.Viewports[0];
4322 g.IO.MetricsRenderWindows++;
4323 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList);
4324 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4325 {
4326 ImGuiWindow* child = window->DC.ChildWindows[i];
4327 if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
4328 AddWindowToDrawData(child, layer);
4329 }
4330}
4331
4332// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
4333static void AddRootWindowToDrawData(ImGuiWindow* window)
4334{
4335 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4336 AddWindowToDrawData(window, layer);
4337}
4338
4339void ImDrawDataBuilder::FlattenIntoSingleLayer()
4340{
4341 int n = Layers[0].Size;
4342 int size = n;
4343 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4344 size += Layers[i].Size;
4345 Layers[0].resize(size);
4346 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4347 {
4348 ImVector<ImDrawList*>& layer = Layers[layer_n];
4349 if (layer.empty())
4350 continue;
4351 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4352 n += layer.Size;
4353 layer.resize(0);
4354 }
4355}
4356
4357static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists)
4358{
4359 ImGuiIO& io = ImGui::GetIO();
4360 ImDrawData* draw_data = &viewport->DrawDataP;
4361 draw_data->Valid = true;
4362 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4363 draw_data->CmdListsCount = draw_lists->Size;
4364 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4365 draw_data->DisplayPos = viewport->Pos;
4366 draw_data->DisplaySize = viewport->Size;
4367 draw_data->FramebufferScale = io.DisplayFramebufferScale;
4368 for (int n = 0; n < draw_lists->Size; n++)
4369 {
4370 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4371 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4372 }
4373}
4374
4375// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4376// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4377// so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4378// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4379// some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4380// more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
4381void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4382{
4383 ImGuiWindow* window = GetCurrentWindow();
4384 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4385 window->ClipRect = window->DrawList->_ClipRectStack.back();
4386}
4387
4388void ImGui::PopClipRect()
4389{
4390 ImGuiWindow* window = GetCurrentWindow();
4391 window->DrawList->PopClipRect();
4392 window->ClipRect = window->DrawList->_ClipRectStack.back();
4393}
4394
4395// 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.
4396void ImGui::EndFrame()
4397{
4398 ImGuiContext& g = *GImGui;
4399 IM_ASSERT(g.Initialized);
4400
4401 // Don't process EndFrame() multiple times.
4402 if (g.FrameCountEnded == g.FrameCount)
4403 return;
4404 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4405
4406 CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
4407
4408 ErrorCheckEndFrameSanityChecks();
4409
4410 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4411 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4412 {
4413 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4414 g.PlatformImeLastPos = g.PlatformImePos;
4415 }
4416
4417 // Hide implicit/fallback "Debug" window if it hasn't been used
4418 g.WithinFrameScopeWithImplicitWindow = false;
4419 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4420 g.CurrentWindow->Active = false;
4421 End();
4422
4423 // Update navigation: CTRL+Tab, wrap-around requests
4424 NavEndFrame();
4425
4426 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4427 if (g.DragDropActive)
4428 {
4429 bool is_delivered = g.DragDropPayload.Delivery;
4430 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4431 if (is_delivered || is_elapsed)
4432 ClearDragDrop();
4433 }
4434
4435 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4436 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4437 {
4438 g.DragDropWithinSource = true;
4439 SetTooltip("...");
4440 g.DragDropWithinSource = false;
4441 }
4442
4443 // End frame
4444 g.WithinFrameScope = false;
4445 g.FrameCountEnded = g.FrameCount;
4446
4447 // Initiate moving window + handle left-click and right-click focus
4448 UpdateMouseMovingWindowEndFrame();
4449
4450 // Sort the window list so that all child windows are after their parent
4451 // We cannot do that on FocusWindow() because children may not exist yet
4452 g.WindowsTempSortBuffer.resize(0);
4453 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4454 for (int i = 0; i != g.Windows.Size; i++)
4455 {
4456 ImGuiWindow* window = g.Windows[i];
4457 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4458 continue;
4459 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4460 }
4461
4462 // 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.
4463 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4464 g.Windows.swap(g.WindowsTempSortBuffer);
4465 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4466
4467 // Unlock font atlas
4468 g.IO.Fonts->Locked = false;
4469
4470 // Clear Input data for next frame
4471 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4472 g.IO.InputQueueCharacters.resize(0);
4473 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4474
4475 CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
4476}
4477
4478// Prepare the data for rendering so you can call GetDrawData()
4479// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:
4480// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
4481void ImGui::Render()
4482{
4483 ImGuiContext& g = *GImGui;
4484 IM_ASSERT(g.Initialized);
4485
4486 if (g.FrameCountEnded != g.FrameCount)
4487 EndFrame();
4488 g.FrameCountRendered = g.FrameCount;
4489 g.IO.MetricsRenderWindows = 0;
4490
4491 CallContextHooks(&g, ImGuiContextHookType_RenderPre);
4492
4493 // Add background ImDrawList (for each active viewport)
4494 for (int n = 0; n != g.Viewports.Size; n++)
4495 {
4496 ImGuiViewportP* viewport = g.Viewports[n];
4497 viewport->DrawDataBuilder.Clear();
4498 if (viewport->DrawLists[0] != NULL)
4499 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
4500 }
4501
4502 // Add ImDrawList to render
4503 ImGuiWindow* windows_to_render_top_most[2];
4504 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4505 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4506 for (int n = 0; n != g.Windows.Size; n++)
4507 {
4508 ImGuiWindow* window = g.Windows[n];
4509 IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
4510 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4511 AddRootWindowToDrawData(window);
4512 }
4513 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4514 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4515 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4516
4517 // Setup ImDrawData structures for end-user
4518 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
4519 for (int n = 0; n < g.Viewports.Size; n++)
4520 {
4521 ImGuiViewportP* viewport = g.Viewports[n];
4522 viewport->DrawDataBuilder.FlattenIntoSingleLayer();
4523
4524 // Draw software mouse cursor if requested by io.MouseDrawCursor flag
4525 if (g.IO.MouseDrawCursor)
4526 RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4527
4528 // Add foreground ImDrawList (for each active viewport)
4529 if (viewport->DrawLists[1] != NULL)
4530 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
4531
4532 SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]);
4533 ImDrawData* draw_data = &viewport->DrawDataP;
4534 g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
4535 g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
4536 }
4537
4538 CallContextHooks(&g, ImGuiContextHookType_RenderPost);
4539}
4540
4541// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4542// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
4543ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4544{
4545 ImGuiContext& g = *GImGui;
4546
4547 const char* text_display_end;
4548 if (hide_text_after_double_hash)
4549 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4550 else
4551 text_display_end = text_end;
4552
4553 ImFont* font = g.Font;
4554 const float font_size = g.FontSize;
4555 if (text == text_display_end)
4556 return ImVec2(0.0f, font_size);
4557 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4558
4559 // Round
4560 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
4561 // FIXME: Investigate using ceilf or e.g.
4562 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
4563 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
4564 text_size.x = IM_FLOOR(text_size.x + 0.99999f);
4565
4566 return text_size;
4567}
4568
4569// Find window given position, search front-to-back
4570// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4571// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4572// called, aka before the next Begin(). Moving window isn't affected.
4573static void FindHoveredWindow()
4574{
4575 ImGuiContext& g = *GImGui;
4576
4577 ImGuiWindow* hovered_window = NULL;
4578 ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4579 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4580 hovered_window = g.MovingWindow;
4581
4582 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4583 ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;
4584 for (int i = g.Windows.Size - 1; i >= 0; i--)
4585 {
4586 ImGuiWindow* window = g.Windows[i];
4587 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4588 if (!window->Active || window->Hidden)
4589 continue;
4590 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4591 continue;
4592
4593 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4594 ImRect bb(window->OuterRectClipped);
4595 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4596 bb.Expand(padding_regular);
4597 else
4598 bb.Expand(padding_for_resize);
4599 if (!bb.Contains(g.IO.MousePos))
4600 continue;
4601
4602 // Support for one rectangular hole in any given window
4603 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4604 if (window->HitTestHoleSize.x != 0)
4605 {
4606 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4607 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4608 if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4609 continue;
4610 }
4611
4612 if (hovered_window == NULL)
4613 hovered_window = window;
4614 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
4615 if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4616 hovered_window_ignoring_moving_window = window;
4617 if (hovered_window && hovered_window_ignoring_moving_window)
4618 break;
4619 }
4620
4621 g.HoveredWindow = hovered_window;
4622 g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4623}
4624
4625// Test if mouse cursor is hovering given rectangle
4626// NB- Rectangle is clipped by our current clip setting
4627// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
4628bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4629{
4630 ImGuiContext& g = *GImGui;
4631
4632 // Clip
4633 ImRect rect_clipped(r_min, r_max);
4634 if (clip)
4635 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4636
4637 // Expand for touch input
4638 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4639 if (!rect_for_touch.Contains(g.IO.MousePos))
4640 return false;
4641 return true;
4642}
4643
4644int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4645{
4646 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4647 ImGuiContext& g = *GImGui;
4648 return g.IO.KeyMap[imgui_key];
4649}
4650
4651// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4652// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
4653bool ImGui::IsKeyDown(int user_key_index)
4654{
4655 if (user_key_index < 0)
4656 return false;
4657 ImGuiContext& g = *GImGui;
4658 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4659 return g.IO.KeysDown[user_key_index];
4660}
4661
4662// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4663// t1 = current time (e.g.: g.Time)
4664// An event is triggered at:
4665// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
4666int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4667{
4668 if (t1 == 0.0f)
4669 return 1;
4670 if (t0 >= t1)
4671 return 0;
4672 if (repeat_rate <= 0.0f)
4673 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4674 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4675 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4676 const int count = count_t1 - count_t0;
4677 return count;
4678}
4679
4680int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4681{
4682 ImGuiContext& g = *GImGui;
4683 if (key_index < 0)
4684 return 0;
4685 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4686 const float t = g.IO.KeysDownDuration[key_index];
4687 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4688}
4689
4690bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4691{
4692 ImGuiContext& g = *GImGui;
4693 if (user_key_index < 0)
4694 return false;
4695 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4696 const float t = g.IO.KeysDownDuration[user_key_index];
4697 if (t == 0.0f)
4698 return true;
4699 if (repeat && t > g.IO.KeyRepeatDelay)
4700 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4701 return false;
4702}
4703
4704bool ImGui::IsKeyReleased(int user_key_index)
4705{
4706 ImGuiContext& g = *GImGui;
4707 if (user_key_index < 0) return false;
4708 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4709 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4710}
4711
4712bool ImGui::IsMouseDown(ImGuiMouseButton button)
4713{
4714 ImGuiContext& g = *GImGui;
4715 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4716 return g.IO.MouseDown[button];
4717}
4718
4719bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4720{
4721 ImGuiContext& g = *GImGui;
4722 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4723 const float t = g.IO.MouseDownDuration[button];
4724 if (t == 0.0f)
4725 return true;
4726
4727 if (repeat && t > g.IO.KeyRepeatDelay)
4728 {
4729 // 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.
4730 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4731 if (amount > 0)
4732 return true;
4733 }
4734 return false;
4735}
4736
4737bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4738{
4739 ImGuiContext& g = *GImGui;
4740 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4741 return g.IO.MouseReleased[button];
4742}
4743
4744bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4745{
4746 ImGuiContext& g = *GImGui;
4747 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4748 return g.IO.MouseDoubleClicked[button];
4749}
4750
4751// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4752// [Internal] This doesn't test if the button is pressed
4753bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4754{
4755 ImGuiContext& g = *GImGui;
4756 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4757 if (lock_threshold < 0.0f)
4758 lock_threshold = g.IO.MouseDragThreshold;
4759 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4760}
4761
4762bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4763{
4764 ImGuiContext& g = *GImGui;
4765 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4766 if (!g.IO.MouseDown[button])
4767 return false;
4768 return IsMouseDragPastThreshold(button, lock_threshold);
4769}
4770
4771ImVec2 ImGui::GetMousePos()
4772{
4773 ImGuiContext& g = *GImGui;
4774 return g.IO.MousePos;
4775}
4776
4777// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
4778ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4779{
4780 ImGuiContext& g = *GImGui;
4781 if (g.BeginPopupStack.Size > 0)
4782 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4783 return g.IO.MousePos;
4784}
4785
4786// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
4787bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4788{
4789 // The assert is only to silence a false-positive in XCode Static Analysis.
4790 // 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).
4791 IM_ASSERT(GImGui != NULL);
4792 const float MOUSE_INVALID = -256000.0f;
4793 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4794 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4795}
4796
4797bool ImGui::IsAnyMouseDown()
4798{
4799 ImGuiContext& g = *GImGui;
4800 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4801 if (g.IO.MouseDown[n])
4802 return true;
4803 return false;
4804}
4805
4806// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4807// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4808// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
4809ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4810{
4811 ImGuiContext& g = *GImGui;
4812 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4813 if (lock_threshold < 0.0f)
4814 lock_threshold = g.IO.MouseDragThreshold;
4815 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4816 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4817 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4818 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4819 return ImVec2(0.0f, 0.0f);
4820}
4821
4822void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4823{
4824 ImGuiContext& g = *GImGui;
4825 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4826 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4827 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4828}
4829
4830ImGuiMouseCursor ImGui::GetMouseCursor()
4831{
4832 return GImGui->MouseCursor;
4833}
4834
4835void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4836{
4837 GImGui->MouseCursor = cursor_type;
4838}
4839
4840void ImGui::CaptureKeyboardFromApp(bool capture)
4841{
4842 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4843}
4844
4845void ImGui::CaptureMouseFromApp(bool capture)
4846{
4847 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4848}
4849
4850bool ImGui::IsItemActive()
4851{
4852 ImGuiContext& g = *GImGui;
4853 if (g.ActiveId)
4854 return g.ActiveId == g.LastItemData.ID;
4855 return false;
4856}
4857
4858bool ImGui::IsItemActivated()
4859{
4860 ImGuiContext& g = *GImGui;
4861 if (g.ActiveId)
4862 if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
4863 return true;
4864 return false;
4865}
4866
4867bool ImGui::IsItemDeactivated()
4868{
4869 ImGuiContext& g = *GImGui;
4870 if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4871 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4872 return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);
4873}
4874
4875bool ImGui::IsItemDeactivatedAfterEdit()
4876{
4877 ImGuiContext& g = *GImGui;
4878 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4879}
4880
4881// == GetItemID() == GetFocusID()
4882bool ImGui::IsItemFocused()
4883{
4884 ImGuiContext& g = *GImGui;
4885 if (g.NavId != g.LastItemData.ID || g.NavId == 0)
4886 return false;
4887 return true;
4888}
4889
4890// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
4891// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
4892bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4893{
4894 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4895}
4896
4897bool ImGui::IsItemToggledOpen()
4898{
4899 ImGuiContext& g = *GImGui;
4900 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4901}
4902
4903bool ImGui::IsItemToggledSelection()
4904{
4905 ImGuiContext& g = *GImGui;
4906 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4907}
4908
4909bool ImGui::IsAnyItemHovered()
4910{
4911 ImGuiContext& g = *GImGui;
4912 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4913}
4914
4915bool ImGui::IsAnyItemActive()
4916{
4917 ImGuiContext& g = *GImGui;
4918 return g.ActiveId != 0;
4919}
4920
4921bool ImGui::IsAnyItemFocused()
4922{
4923 ImGuiContext& g = *GImGui;
4924 return g.NavId != 0 && !g.NavDisableHighlight;
4925}
4926
4927bool ImGui::IsItemVisible()
4928{
4929 ImGuiContext& g = *GImGui;
4930 return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect);
4931}
4932
4933bool ImGui::IsItemEdited()
4934{
4935 ImGuiContext& g = *GImGui;
4936 return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4937}
4938
4939// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
4940// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework.
4941void ImGui::SetItemAllowOverlap()
4942{
4943 ImGuiContext& g = *GImGui;
4944 ImGuiID id = g.LastItemData.ID;
4945 if (g.HoveredId == id)
4946 g.HoveredIdAllowOverlap = true;
4947 if (g.ActiveId == id)
4948 g.ActiveIdAllowOverlap = true;
4949}
4950
4951void ImGui::SetItemUsingMouseWheel()
4952{
4953 ImGuiContext& g = *GImGui;
4954 ImGuiID id = g.LastItemData.ID;
4955 if (g.HoveredId == id)
4956 g.HoveredIdUsingMouseWheel = true;
4957 if (g.ActiveId == id)
4958 g.ActiveIdUsingMouseWheel = true;
4959}
4960
4961void ImGui::SetActiveIdUsingNavAndKeys()
4962{
4963 ImGuiContext& g = *GImGui;
4964 IM_ASSERT(g.ActiveId != 0);
4965 g.ActiveIdUsingNavDirMask = ~(ImU32)0;
4966 g.ActiveIdUsingNavInputMask = ~(ImU32)0;
4967 g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
4968 NavMoveRequestCancel();
4969}
4970
4971ImVec2 ImGui::GetItemRectMin()
4972{
4973 ImGuiContext& g = *GImGui;
4974 return g.LastItemData.Rect.Min;
4975}
4976
4977ImVec2 ImGui::GetItemRectMax()
4978{
4979 ImGuiContext& g = *GImGui;
4980 return g.LastItemData.Rect.Max;
4981}
4982
4983ImVec2 ImGui::GetItemRectSize()
4984{
4985 ImGuiContext& g = *GImGui;
4986 return g.LastItemData.Rect.GetSize();
4987}
4988
4989bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4990{
4991 ImGuiContext& g = *GImGui;
4992 ImGuiWindow* parent_window = g.CurrentWindow;
4993
4994 flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
4995 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4996
4997 // Size
4998 const ImVec2 content_avail = GetContentRegionAvail();
4999 ImVec2 size = ImFloor(size_arg);
5000 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
5001 if (size.x <= 0.0f)
5002 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
5003 if (size.y <= 0.0f)
5004 size.y = ImMax(content_avail.y + size.y, 4.0f);
5005 SetNextWindowSize(size);
5006
5007 // 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.
5008 if (name)
5009 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id);
5010 else
5011 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id);
5012
5013 const float backup_border_size = g.Style.ChildBorderSize;
5014 if (!border)
5015 g.Style.ChildBorderSize = 0.0f;
5016 bool ret = Begin(g.TempBuffer, NULL, flags);
5017 g.Style.ChildBorderSize = backup_border_size;
5018
5019 ImGuiWindow* child_window = g.CurrentWindow;
5020 child_window->ChildId = id;
5021 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
5022
5023 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
5024 // While this is not really documented/defined, it seems that the expected thing to do.
5025 if (child_window->BeginCount == 1)
5026 parent_window->DC.CursorPos = child_window->Pos;
5027
5028 // Process navigation-in immediately so NavInit can run on first frame
5029 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll))
5030 {
5031 FocusWindow(child_window);
5032 NavInitWindow(child_window, false);
5033 SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
5034 g.ActiveIdSource = ImGuiInputSource_Nav;
5035 }
5036 return ret;
5037}
5038
5039bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5040{
5041 ImGuiWindow* window = GetCurrentWindow();
5042 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
5043}
5044
5045bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
5046{
5047 IM_ASSERT(id != 0);
5048 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
5049}
5050
5051void ImGui::EndChild()
5052{
5053 ImGuiContext& g = *GImGui;
5054 ImGuiWindow* window = g.CurrentWindow;
5055
5056 IM_ASSERT(g.WithinEndChild == false);
5057 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
5058
5059 g.WithinEndChild = true;
5060 if (window->BeginCount > 1)
5061 {
5062 End();
5063 }
5064 else
5065 {
5066 ImVec2 sz = window->Size;
5067 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
5068 sz.x = ImMax(4.0f, sz.x);
5069 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
5070 sz.y = ImMax(4.0f, sz.y);
5071 End();
5072
5073 ImGuiWindow* parent_window = g.CurrentWindow;
5074 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
5075 ItemSize(sz);
5076 if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
5077 {
5078 ItemAdd(bb, window->ChildId);
5079 RenderNavHighlight(bb, window->ChildId);
5080
5081 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
5082 if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow)
5083 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
5084 }
5085 else
5086 {
5087 // Not navigable into
5088 ItemAdd(bb, 0);
5089 }
5090 if (g.HoveredWindow == window)
5091 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
5092 }
5093 g.WithinEndChild = false;
5094 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
5095}
5096
5097// Helper to create a child window / scrolling region that looks like a normal widget frame.
5098bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
5099{
5100 ImGuiContext& g = *GImGui;
5101 const ImGuiStyle& style = g.Style;
5102 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
5103 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
5104 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
5105 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
5106 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
5107 PopStyleVar(3);
5108 PopStyleColor();
5109 return ret;
5110}
5111
5112void ImGui::EndChildFrame()
5113{
5114 EndChild();
5115}
5116
5117static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
5118{
5119 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
5120 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
5121 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
5122}
5123
5124ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
5125{
5126 ImGuiContext& g = *GImGui;
5127 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
5128}
5129
5130ImGuiWindow* ImGui::FindWindowByName(const char* name)
5131{
5132 ImGuiID id = ImHashStr(name);
5133 return FindWindowByID(id);
5134}
5135
5136static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
5137{
5138 window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
5139 if (settings->Size.x > 0 && settings->Size.y > 0)
5140 window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
5141 window->Collapsed = settings->Collapsed;
5142}
5143
5144static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
5145{
5146 ImGuiContext& g = *GImGui;
5147 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
5148
5149 // Create window the first time
5150 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
5151 window->Flags = flags;
5152 g.WindowsById.SetVoidPtr(window->ID, window);
5153
5154 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
5155 const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
5156 window->Pos = main_viewport->Pos + ImVec2(60, 60);
5157
5158 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
5159 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
5160 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
5161 {
5162 // Retrieve settings from .ini file
5163 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
5164 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
5165 ApplyWindowSettings(window, settings);
5166 }
5167 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
5168
5169 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
5170 {
5171 window->AutoFitFramesX = window->AutoFitFramesY = 2;
5172 window->AutoFitOnlyGrows = false;
5173 }
5174 else
5175 {
5176 if (window->Size.x <= 0.0f)
5177 window->AutoFitFramesX = 2;
5178 if (window->Size.y <= 0.0f)
5179 window->AutoFitFramesY = 2;
5180 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
5181 }
5182
5183 if (!(flags & ImGuiWindowFlags_ChildWindow))
5184 {
5185 g.WindowsFocusOrder.push_back(window);
5186 window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
5187 }
5188
5189 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
5190 g.Windows.push_front(window); // Quite slow but rare and only once
5191 else
5192 g.Windows.push_back(window);
5193 return window;
5194}
5195
5196static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
5197{
5198 ImGuiContext& g = *GImGui;
5199 ImVec2 new_size = size_desired;
5200 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
5201 {
5202 // Using -1,-1 on either X/Y axis to preserve the current size.
5203 ImRect cr = g.NextWindowData.SizeConstraintRect;
5204 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
5205 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
5206 if (g.NextWindowData.SizeCallback)
5207 {
5208 ImGuiSizeCallbackData data;
5209 data.UserData = g.NextWindowData.SizeCallbackUserData;
5210 data.Pos = window->Pos;
5211 data.CurrentSize = window->SizeFull;
5212 data.DesiredSize = new_size;
5213 g.NextWindowData.SizeCallback(&data);
5214 new_size = data.DesiredSize;
5215 }
5216 new_size.x = IM_FLOOR(new_size.x);
5217 new_size.y = IM_FLOOR(new_size.y);
5218 }
5219
5220 // Minimum size
5221 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
5222 {
5223 ImGuiWindow* window_for_height = window;
5224 const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight();
5225 new_size = ImMax(new_size, g.Style.WindowMinSize);
5226 new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
5227 }
5228 return new_size;
5229}
5230
5231static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
5232{
5233 bool preserve_old_content_sizes = false;
5234 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5235 preserve_old_content_sizes = true;
5236 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
5237 preserve_old_content_sizes = true;
5238 if (preserve_old_content_sizes)
5239 {
5240 *content_size_current = window->ContentSize;
5241 *content_size_ideal = window->ContentSizeIdeal;
5242 return;
5243 }
5244
5245 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
5246 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
5247 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);
5248 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);
5249}
5250
5251static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
5252{
5253 ImGuiContext& g = *GImGui;
5254 ImGuiStyle& style = g.Style;
5255 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5256 ImVec2 size_pad = window->WindowPadding * 2.0f;
5257 ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height);
5258 if (window->Flags & ImGuiWindowFlags_Tooltip)
5259 {
5260 // Tooltip always resize
5261 return size_desired;
5262 }
5263 else
5264 {
5265 // Maximum window size is determined by the viewport size or monitor size
5266 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
5267 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
5268 ImVec2 size_min = style.WindowMinSize;
5269 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)
5270 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
5271
5272 // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows?
5273 ImVec2 avail_size = ImGui::GetMainViewport()->Size;
5274 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));
5275
5276 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
5277 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
5278 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5279 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);
5280 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);
5281 if (will_have_scrollbar_x)
5282 size_auto_fit.y += style.ScrollbarSize;
5283 if (will_have_scrollbar_y)
5284 size_auto_fit.x += style.ScrollbarSize;
5285 return size_auto_fit;
5286 }
5287}
5288
5289ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
5290{
5291 ImVec2 size_contents_current;
5292 ImVec2 size_contents_ideal;
5293 CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
5294 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
5295 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5296 return size_final;
5297}
5298
5299static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5300{
5301 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5302 return ImGuiCol_PopupBg;
5303 if (flags & ImGuiWindowFlags_ChildWindow)
5304 return ImGuiCol_ChildBg;
5305 return ImGuiCol_WindowBg;
5306}
5307
5308static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5309{
5310 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
5311 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5312 ImVec2 size_expected = pos_max - pos_min;
5313 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5314 *out_pos = pos_min;
5315 if (corner_norm.x == 0.0f)
5316 out_pos->x -= (size_constrained.x - size_expected.x);
5317 if (corner_norm.y == 0.0f)
5318 out_pos->y -= (size_constrained.y - size_expected.y);
5319 *out_size = size_constrained;
5320}
5321
5322// Data for resizing from corner
5323struct ImGuiResizeGripDef
5324{
5325 ImVec2 CornerPosN;
5326 ImVec2 InnerDir;
5327 int AngleMin12, AngleMax12;
5328};
5329static const ImGuiResizeGripDef resize_grip_def[4] =
5330{
5331 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5332 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5333 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5334 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
5335};
5336
5337// Data for resizing from borders
5338struct ImGuiResizeBorderDef
5339{
5340 ImVec2 InnerDir;
5341 ImVec2 SegmentN1, SegmentN2;
5342 float OuterAngle;
5343};
5344static const ImGuiResizeBorderDef resize_border_def[4] =
5345{
5346 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
5347 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5348 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
5349 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down
5350};
5351
5352static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5353{
5354 ImRect rect = window->Rect();
5355 if (thickness == 0.0f)
5356 rect.Max -= ImVec2(1, 1);
5357 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); }
5358 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); }
5359 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); }
5360 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); }
5361 IM_ASSERT(0);
5362 return ImRect();
5363}
5364
5365// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
5366ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
5367{
5368 IM_ASSERT(n >= 0 && n < 4);
5369 ImGuiID id = window->ID;
5370 id = ImHashStr("#RESIZE", 0, id);
5371 id = ImHashData(&n, sizeof(int), id);
5372 return id;
5373}
5374
5375// Borders (Left, Right, Up, Down)
5376ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
5377{
5378 IM_ASSERT(dir >= 0 && dir < 4);
5379 int n = (int)dir + 4;
5380 ImGuiID id = window->ID;
5381 id = ImHashStr("#RESIZE", 0, id);
5382 id = ImHashData(&n, sizeof(int), id);
5383 return id;
5384}
5385
5386// Handle resize for: Resize Grips, Borders, Gamepad
5387// Return true when using auto-fit (double click on resize grip)
5388static 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)
5389{
5390 ImGuiContext& g = *GImGui;
5391 ImGuiWindowFlags flags = window->Flags;
5392
5393 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5394 return false;
5395 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5396 return false;
5397
5398 bool ret_auto_fit = false;
5399 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5400 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5401 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5402 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;
5403
5404 ImVec2 pos_target(FLT_MAX, FLT_MAX);
5405 ImVec2 size_target(FLT_MAX, FLT_MAX);
5406
5407 // Resize grips and borders are on layer 1
5408 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5409
5410 // Manual resize grips
5411 PushID("#RESIZE");
5412 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5413 {
5414 const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
5415 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
5416
5417 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5418 bool hovered, held;
5419 ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
5420 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5421 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5422 ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
5423 ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5424 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5425 if (hovered || held)
5426 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5427
5428 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5429 {
5430 // Manual auto-fit when double-clicking
5431 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5432 ret_auto_fit = true;
5433 ClearActiveID();
5434 }
5435 else if (held)
5436 {
5437 // Resize from any of the four corners
5438 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5439 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);
5440 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);
5441 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
5442 corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5443 CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
5444 }
5445
5446 // Only lower-left grip is visible before hovering/activating
5447 if (resize_grip_n == 0 || held || hovered)
5448 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5449 }
5450 for (int border_n = 0; border_n < resize_border_count; border_n++)
5451 {
5452 const ImGuiResizeBorderDef& def = resize_border_def[border_n];
5453 const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
5454
5455 bool hovered, held;
5456 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);
5457 ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
5458 ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5459 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5460 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5461 {
5462 g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5463 if (held)
5464 *border_held = border_n;
5465 }
5466 if (held)
5467 {
5468 ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX);
5469 ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX);
5470 ImVec2 border_target = window->Pos;
5471 border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING;
5472 border_target = ImClamp(border_target, clamp_min, clamp_max);
5473 CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
5474 }
5475 }
5476 PopID();
5477
5478 // Restore nav layer
5479 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5480
5481 // Navigation resize (keyboard/gamepad)
5482 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5483 {
5484 ImVec2 nav_resize_delta;
5485 if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
5486 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5487 if (g.NavInputSource == ImGuiInputSource_Gamepad)
5488 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5489 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5490 {
5491 const float NAV_RESIZE_SPEED = 600.0f;
5492 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5493 nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5494 g.NavWindowingToggleLayer = false;
5495 g.NavDisableMouseHover = true;
5496 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5497 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5498 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5499 }
5500 }
5501
5502 // Apply back modified position/size to window
5503 if (size_target.x != FLT_MAX)
5504 {
5505 window->SizeFull = size_target;
5506 MarkIniSettingsDirty(window);
5507 }
5508 if (pos_target.x != FLT_MAX)
5509 {
5510 window->Pos = ImFloor(pos_target);
5511 MarkIniSettingsDirty(window);
5512 }
5513
5514 window->Size = window->SizeFull;
5515 return ret_auto_fit;
5516}
5517
5518static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5519{
5520 ImGuiContext& g = *GImGui;
5521 ImVec2 size_for_clamping = window->Size;
5522 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5523 size_for_clamping.y = window->TitleBarHeight();
5524 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5525}
5526
5527static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5528{
5529 ImGuiContext& g = *GImGui;
5530 float rounding = window->WindowRounding;
5531 float border_size = window->WindowBorderSize;
5532 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5533 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
5534
5535 int border_held = window->ResizeBorderHeld;
5536 if (border_held != -1)
5537 {
5538 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5539 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5540 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);
5541 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);
5542 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual
5543 }
5544 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5545 {
5546 float y = window->Pos.y + window->TitleBarHeight() - 1;
5547 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);
5548 }
5549}
5550
5551// Draw background and borders
5552// Draw and handle scrollbars
5553void 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)
5554{
5555 ImGuiContext& g = *GImGui;
5556 ImGuiStyle& style = g.Style;
5557 ImGuiWindowFlags flags = window->Flags;
5558
5559 // Ensure that ScrollBar doesn't read last frame's SkipItems
5560 IM_ASSERT(window->BeginCount == 0);
5561 window->SkipItems = false;
5562
5563 // Draw window + handle manual resize
5564 // 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.
5565 const float window_rounding = window->WindowRounding;
5566 const float window_border_size = window->WindowBorderSize;
5567 if (window->Collapsed)
5568 {
5569 // Title bar only
5570 float backup_border_size = style.FrameBorderSize;
5571 g.Style.FrameBorderSize = window->WindowBorderSize;
5572 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5573 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5574 g.Style.FrameBorderSize = backup_border_size;
5575 }
5576 else
5577 {
5578 // Window background
5579 if (!(flags & ImGuiWindowFlags_NoBackground))
5580 {
5581 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5582 bool override_alpha = false;
5583 float alpha = 1.0f;
5584 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5585 {
5586 alpha = g.NextWindowData.BgAlphaVal;
5587 override_alpha = true;
5588 }
5589 if (override_alpha)
5590 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5591 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
5592 }
5593
5594 // Title bar
5595 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5596 {
5597 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5598 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
5599 }
5600
5601 // Menu bar
5602 if (flags & ImGuiWindowFlags_MenuBar)
5603 {
5604 ImRect menu_bar_rect = window->MenuBarRect();
5605 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.
5606 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);
5607 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5608 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5609 }
5610
5611 // Scrollbars
5612 if (window->ScrollbarX)
5613 Scrollbar(ImGuiAxis_X);
5614 if (window->ScrollbarY)
5615 Scrollbar(ImGuiAxis_Y);
5616
5617 // Render resize grips (after their input handling so we don't have a frame of latency)
5618 if (!(flags & ImGuiWindowFlags_NoResize))
5619 {
5620 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5621 {
5622 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5623 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5624 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)));
5625 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)));
5626 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);
5627 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5628 }
5629 }
5630
5631 // Borders
5632 RenderWindowOuterBorders(window);
5633 }
5634}
5635
5636// Render title text, collapse button, close button
5637void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5638{
5639 ImGuiContext& g = *GImGui;
5640 ImGuiStyle& style = g.Style;
5641 ImGuiWindowFlags flags = window->Flags;
5642
5643 const bool has_close_button = (p_open != NULL);
5644 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5645
5646 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5647 const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
5648 g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5649 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5650
5651 // Layout buttons
5652 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5653 float pad_l = style.FramePadding.x;
5654 float pad_r = style.FramePadding.x;
5655 float button_sz = g.FontSize;
5656 ImVec2 close_button_pos;
5657 ImVec2 collapse_button_pos;
5658 if (has_close_button)
5659 {
5660 pad_r += button_sz;
5661 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5662 }
5663 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5664 {
5665 pad_r += button_sz;
5666 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5667 }
5668 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5669 {
5670 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5671 pad_l += button_sz;
5672 }
5673
5674 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5675 if (has_collapse_button)
5676 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5677 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5678
5679 // Close button
5680 if (has_close_button)
5681 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5682 *p_open = false;
5683
5684 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5685 g.CurrentItemFlags = item_flags_backup;
5686
5687 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5688 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5689 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
5690 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5691
5692 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5693 // while uncentered title text will still reach edges correctly.
5694 if (pad_l > style.FramePadding.x)
5695 pad_l += g.Style.ItemInnerSpacing.x;
5696 if (pad_r > style.FramePadding.x)
5697 pad_r += g.Style.ItemInnerSpacing.x;
5698 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5699 {
5700 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5701 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5702 pad_l = ImMax(pad_l, pad_extend * centerness);
5703 pad_r = ImMax(pad_r, pad_extend * centerness);
5704 }
5705
5706 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);
5707 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);
5708 if (flags & ImGuiWindowFlags_UnsavedDocument)
5709 {
5710 ImVec2 marker_pos;
5711 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);
5712 marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
5713 if (marker_pos.x > layout_r.Min.x)
5714 {
5715 RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));
5716 clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
5717 }
5718 }
5719 //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5720 //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5721 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5722}
5723
5724void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5725{
5726 window->ParentWindow = parent_window;
5727 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5728 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5729 window->RootWindow = parent_window->RootWindow;
5730 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5731 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5732 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5733 {
5734 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5735 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5736 }
5737}
5738
5739// Push a new Dear ImGui window to add widgets to.
5740// - 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.
5741// - Begin/End can be called multiple times during the frame with the same window name to append content.
5742// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5743// 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.
5744// - 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.
5745// - 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.
5746bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5747{
5748 ImGuiContext& g = *GImGui;
5749 const ImGuiStyle& style = g.Style;
5750 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5751 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
5752 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5753
5754 // Find or create
5755 ImGuiWindow* window = FindWindowByName(name);
5756 const bool window_just_created = (window == NULL);
5757 if (window_just_created)
5758 window = CreateNewWindow(name, flags);
5759
5760 // Automatically disable manual moving/resizing when NoInputs is set
5761 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5762 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5763
5764 if (flags & ImGuiWindowFlags_NavFlattened)
5765 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5766
5767 const int current_frame = g.FrameCount;
5768 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5769 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5770
5771 // Update the Appearing flag
5772 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5773 if (flags & ImGuiWindowFlags_Popup)
5774 {
5775 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5776 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5777 window_just_activated_by_user |= (window != popup_ref.Window);
5778 }
5779 window->Appearing = window_just_activated_by_user;
5780 if (window->Appearing)
5781 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5782
5783 // Update Flags, LastFrameActive, BeginOrderXXX fields
5784 if (first_begin_of_the_frame)
5785 {
5786 window->Flags = (ImGuiWindowFlags)flags;
5787 window->LastFrameActive = current_frame;
5788 window->LastTimeActive = (float)g.Time;
5789 window->BeginOrderWithinParent = 0;
5790 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5791 }
5792 else
5793 {
5794 flags = window->Flags;
5795 }
5796
5797 // 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
5798 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
5799 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5800 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5801
5802 // We allow window memory to be compacted so recreate the base stack when needed.
5803 if (window->IDStack.Size == 0)
5804 window->IDStack.push_back(window->ID);
5805
5806 // Add to stack
5807 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5808 ImGuiWindowStackData window_stack_data;
5809 window_stack_data.Window = window;
5810 window_stack_data.ParentLastItemDataBackup = g.LastItemData;
5811 g.CurrentWindowStack.push_back(window_stack_data);
5812 g.CurrentWindow = window;
5813 window->DC.StackSizesOnBegin.SetToCurrentState();
5814 g.CurrentWindow = NULL;
5815
5816 if (flags & ImGuiWindowFlags_Popup)
5817 {
5818 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5819 popup_ref.Window = window;
5820 g.BeginPopupStack.push_back(popup_ref);
5821 window->PopupId = popup_ref.PopupId;
5822 }
5823
5824 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5825 if (first_begin_of_the_frame)
5826 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5827
5828 // Process SetNextWindow***() calls
5829 // (FIXME: Consider splitting the HasXXX flags into X/Y components
5830 bool window_pos_set_by_api = false;
5831 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5832 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5833 {
5834 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5835 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5836 {
5837 // May be processed on the next frame if this is our first frame and we are measuring size
5838 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5839 window->SetWindowPosVal = g.NextWindowData.PosVal;
5840 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5841 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5842 }
5843 else
5844 {
5845 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5846 }
5847 }
5848 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5849 {
5850 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5851 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5852 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5853 }
5854 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5855 {
5856 if (g.NextWindowData.ScrollVal.x >= 0.0f)
5857 {
5858 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5859 window->ScrollTargetCenterRatio.x = 0.0f;
5860 }
5861 if (g.NextWindowData.ScrollVal.y >= 0.0f)
5862 {
5863 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5864 window->ScrollTargetCenterRatio.y = 0.0f;
5865 }
5866 }
5867 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5868 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5869 else if (first_begin_of_the_frame)
5870 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5871 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5872 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5873 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5874 FocusWindow(window);
5875 if (window->Appearing)
5876 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5877
5878 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5879 if (first_begin_of_the_frame)
5880 {
5881 // Initialize
5882 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5883 window->Active = true;
5884 window->HasCloseButton = (p_open != NULL);
5885 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5886 window->IDStack.resize(1);
5887 window->DrawList->_ResetForNewFrame();
5888 window->DC.CurrentTableIdx = -1;
5889
5890 // Restore buffer capacity when woken from a compacted state, to avoid
5891 if (window->MemoryCompacted)
5892 GcAwakeTransientWindowBuffers(window);
5893
5894 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5895 // 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.
5896 bool window_title_visible_elsewhere = false;
5897 if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5898 window_title_visible_elsewhere = true;
5899 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5900 {
5901 size_t buf_len = (size_t)window->NameBufLen;
5902 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5903 window->NameBufLen = (int)buf_len;
5904 }
5905
5906 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5907
5908 // Update contents size from last frame for auto-fitting (or use explicit size)
5909 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5910 CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
5911 if (window->HiddenFramesCanSkipItems > 0)
5912 window->HiddenFramesCanSkipItems--;
5913 if (window->HiddenFramesCannotSkipItems > 0)
5914 window->HiddenFramesCannotSkipItems--;
5915 if (window->HiddenFramesForRenderOnly > 0)
5916 window->HiddenFramesForRenderOnly--;
5917
5918 // Hide new windows for one frame until they calculate their size
5919 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5920 window->HiddenFramesCannotSkipItems = 1;
5921
5922 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5923 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5924 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5925 {
5926 window->HiddenFramesCannotSkipItems = 1;
5927 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5928 {
5929 if (!window_size_x_set_by_api)
5930 window->Size.x = window->SizeFull.x = 0.f;
5931 if (!window_size_y_set_by_api)
5932 window->Size.y = window->SizeFull.y = 0.f;
5933 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
5934 }
5935 }
5936
5937 // SELECT VIEWPORT
5938 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5939 SetCurrentWindow(window);
5940
5941 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5942
5943 if (flags & ImGuiWindowFlags_ChildWindow)
5944 window->WindowBorderSize = style.ChildBorderSize;
5945 else
5946 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5947 window->WindowPadding = style.WindowPadding;
5948 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5949 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5950
5951 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
5952 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5953 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5954
5955 // Collapse window by double-clicking on title bar
5956 // 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
5957 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5958 {
5959 // 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.
5960 ImRect title_bar_rect = window->TitleBarRect();
5961 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5962 window->WantCollapseToggle = true;
5963 if (window->WantCollapseToggle)
5964 {
5965 window->Collapsed = !window->Collapsed;
5966 MarkIniSettingsDirty(window);
5967 }
5968 }
5969 else
5970 {
5971 window->Collapsed = false;
5972 }
5973 window->WantCollapseToggle = false;
5974
5975 // SIZE
5976
5977 // Calculate auto-fit size, handle automatic resize
5978 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
5979 bool use_current_size_for_scrollbar_x = window_just_created;
5980 bool use_current_size_for_scrollbar_y = window_just_created;
5981 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5982 {
5983 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5984 if (!window_size_x_set_by_api)
5985 {
5986 window->SizeFull.x = size_auto_fit.x;
5987 use_current_size_for_scrollbar_x = true;
5988 }
5989 if (!window_size_y_set_by_api)
5990 {
5991 window->SizeFull.y = size_auto_fit.y;
5992 use_current_size_for_scrollbar_y = true;
5993 }
5994 }
5995 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5996 {
5997 // Auto-fit may only grow window during the first few frames
5998 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5999 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
6000 {
6001 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
6002 use_current_size_for_scrollbar_x = true;
6003 }
6004 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
6005 {
6006 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
6007 use_current_size_for_scrollbar_y = true;
6008 }
6009 if (!window->Collapsed)
6010 MarkIniSettingsDirty(window);
6011 }
6012
6013 // Apply minimum/maximum window size constraints and final size
6014 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
6015 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
6016
6017 // Decoration size
6018 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
6019
6020 // POSITION
6021
6022 // Popup latch its initial position, will position itself when it appears next frame
6023 if (window_just_activated_by_user)
6024 {
6025 window->AutoPosLastDirection = ImGuiDir_None;
6026 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
6027 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
6028 }
6029
6030 // Position child window
6031 if (flags & ImGuiWindowFlags_ChildWindow)
6032 {
6033 IM_ASSERT(parent_window && parent_window->Active);
6034 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
6035 parent_window->DC.ChildWindows.push_back(window);
6036 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
6037 window->Pos = parent_window->DC.CursorPos;
6038 }
6039
6040 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
6041 if (window_pos_with_pivot)
6042 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
6043 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
6044 window->Pos = FindBestWindowPosForPopup(window);
6045 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
6046 window->Pos = FindBestWindowPosForPopup(window);
6047 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
6048 window->Pos = FindBestWindowPosForPopup(window);
6049
6050 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
6051 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
6052 ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
6053 ImRect viewport_rect(viewport->GetMainRect());
6054 ImRect viewport_work_rect(viewport->GetWorkRect());
6055 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
6056 ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
6057
6058 // Clamp position/size so window stays visible within its viewport or monitor
6059 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
6060 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6061 if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
6062 ClampWindowRect(window, visibility_rect);
6063 window->Pos = ImFloor(window->Pos);
6064
6065 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
6066 // Large values tend to lead to variety of artifacts and are not recommended.
6067 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
6068
6069 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
6070 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6071 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
6072
6073 // Apply window focus (new and reactivated windows are moved to front)
6074 bool want_focus = false;
6075 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
6076 {
6077 if (flags & ImGuiWindowFlags_Popup)
6078 want_focus = true;
6079 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
6080 want_focus = true;
6081 }
6082
6083 // Handle manual resize: Resize Grips, Borders, Gamepad
6084 int border_held = -1;
6085 ImU32 resize_grip_col[4] = {};
6086 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
6087 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6088 if (!window->Collapsed)
6089 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
6090 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
6091 window->ResizeBorderHeld = (signed char)border_held;
6092
6093 // SCROLLBAR VISIBILITY
6094
6095 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
6096 if (!window->Collapsed)
6097 {
6098 // When reading the current size we need to read it after size constraints have been applied.
6099 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
6100 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
6101 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
6102 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
6103 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
6104 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
6105 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
6106 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
6107 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));
6108 if (window->ScrollbarX && !window->ScrollbarY)
6109 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
6110 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
6111 }
6112
6113 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
6114 // Update various regions. Variables they depends on should be set above in this function.
6115 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
6116
6117 // Outer rectangle
6118 // Not affected by window border size. Used by:
6119 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
6120 // - Begin() initial clipping rect for drawing window background and borders.
6121 // - Begin() clipping whole child
6122 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
6123 const ImRect outer_rect = window->Rect();
6124 const ImRect title_bar_rect = window->TitleBarRect();
6125 window->OuterRectClipped = outer_rect;
6126 window->OuterRectClipped.ClipWith(host_rect);
6127
6128 // Inner rectangle
6129 // Not affected by window border size. Used by:
6130 // - InnerClipRect
6131 // - ScrollToBringRectIntoView()
6132 // - NavUpdatePageUpPageDown()
6133 // - Scrollbar()
6134 window->InnerRect.Min.x = window->Pos.x;
6135 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
6136 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
6137 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
6138
6139 // Inner clipping rectangle.
6140 // Will extend a little bit outside the normal work region.
6141 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
6142 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
6143 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
6144 // Affected by window/frame border size. Used by:
6145 // - Begin() initial clip rect
6146 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
6147 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6148 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
6149 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
6150 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
6151 window->InnerClipRect.ClipWithFull(host_rect);
6152
6153 // Default item width. Make it proportional to window size if window manually resizes
6154 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
6155 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
6156 else
6157 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
6158
6159 // SCROLLING
6160
6161 // Lock down maximum scrolling
6162 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
6163 // for right/bottom aligned items without creating a scrollbar.
6164 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
6165 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
6166
6167 // Apply scrolling
6168 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
6169 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
6170
6171 // DRAWING
6172
6173 // Setup draw list and outer clipping rectangle
6174 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
6175 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
6176 PushClipRect(host_rect.Min, host_rect.Max, false);
6177
6178 // Draw modal window background (darkens what is behind them, all viewports)
6179 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
6180 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
6181 if (dim_bg_for_modal || dim_bg_for_window_list)
6182 {
6183 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
6184 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
6185 }
6186
6187 // Draw navigation selection/windowing rectangle background
6188 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
6189 {
6190 ImRect bb = window->Rect();
6191 bb.Expand(g.FontSize);
6192 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
6193 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
6194 }
6195
6196 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
6197 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
6198 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
6199 // We also disabled this when we have dimming overlay behind this specific one child.
6200 // FIXME: More 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.
6201 {
6202 bool render_decorations_in_parent = false;
6203 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
6204 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
6205 render_decorations_in_parent = true;
6206 if (render_decorations_in_parent)
6207 window->DrawList = parent_window->DrawList;
6208
6209 // Handle title bar, scrollbar, resize grips and resize borders
6210 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
6211 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
6212 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
6213
6214 if (render_decorations_in_parent)
6215 window->DrawList = &window->DrawListInst;
6216 }
6217
6218 // Draw navigation selection/windowing rectangle border
6219 if (g.NavWindowingTargetAnim == window)
6220 {
6221 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
6222 ImRect bb = window->Rect();
6223 bb.Expand(g.FontSize);
6224 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
6225 {
6226 bb.Expand(-g.FontSize - 1.0f);
6227 rounding = window->WindowRounding;
6228 }
6229 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f);
6230 }
6231
6232 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
6233
6234 // Work rectangle.
6235 // Affected by window padding and border size. Used by:
6236 // - Columns() for right-most edge
6237 // - TreeNode(), CollapsingHeader() for right-most edge
6238 // - BeginTabBar() for right-most edge
6239 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
6240 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
6241 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));
6242 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));
6243 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
6244 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
6245 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
6246 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
6247 window->ParentWorkRect = window->WorkRect;
6248
6249 // [LEGACY] Content Region
6250 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
6251 // Used by:
6252 // - Mouse wheel scrolling + many other things
6253 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
6254 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
6255 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));
6256 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));
6257
6258 // Setup drawing context
6259 // (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.)
6260 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
6261 window->DC.GroupOffset.x = 0.0f;
6262 window->DC.ColumnsOffset.x = 0.0f;
6263 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
6264 window->DC.CursorPos = window->DC.CursorStartPos;
6265 window->DC.CursorPosPrevLine = window->DC.CursorPos;
6266 window->DC.CursorMaxPos = window->DC.CursorStartPos;
6267 window->DC.IdealMaxPos = window->DC.CursorStartPos;
6268 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
6269 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
6270
6271 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6272 window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
6273 window->DC.NavLayersActiveMaskNext = 0x00;
6274 window->DC.NavHideHighlightOneFrame = false;
6275 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
6276
6277 window->DC.MenuBarAppending = false;
6278 window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
6279 window->DC.TreeDepth = 0;
6280 window->DC.TreeJumpToParentOnPopMask = 0x00;
6281 window->DC.ChildWindows.resize(0);
6282 window->DC.StateStorage = &window->StateStorage;
6283 window->DC.CurrentColumns = NULL;
6284 window->DC.LayoutType = ImGuiLayoutType_Vertical;
6285 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
6286 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
6287
6288 window->DC.ItemWidth = window->ItemWidthDefault;
6289 window->DC.TextWrapPos = -1.0f; // disabled
6290 window->DC.ItemWidthStack.resize(0);
6291 window->DC.TextWrapPosStack.resize(0);
6292
6293 if (window->AutoFitFramesX > 0)
6294 window->AutoFitFramesX--;
6295 if (window->AutoFitFramesY > 0)
6296 window->AutoFitFramesY--;
6297
6298 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
6299 if (want_focus)
6300 {
6301 FocusWindow(window);
6302 NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
6303 }
6304
6305 // Title bar
6306 if (!(flags & ImGuiWindowFlags_NoTitleBar))
6307 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);
6308
6309 // Clear hit test shape every frame
6310 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6311
6312 // Pressing CTRL+C while holding on a window copy its content to the clipboard
6313 // 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.
6314 // Maybe we can support CTRL+C on every element?
6315 /*
6316 //if (g.NavWindow == window && g.ActiveId == 0)
6317 if (g.ActiveId == window->MoveId)
6318 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6319 LogToClipboard();
6320 */
6321
6322 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6323 // This is useful to allow creating context menus on title bar only, etc.
6324 g.LastItemData.ID = window->MoveId;
6325 g.LastItemData.InFlags = g.CurrentItemFlags;
6326 g.LastItemData.StatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
6327 g.LastItemData.Rect = title_bar_rect;
6328
6329#ifdef IMGUI_ENABLE_TEST_ENGINE
6330 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6331 IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID);
6332#endif
6333 }
6334 else
6335 {
6336 // Append
6337 SetCurrentWindow(window);
6338 }
6339
6340 // Pull/inherit current state
6341 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595
6342
6343 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6344
6345 // 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)
6346 window->WriteAccessed = false;
6347 window->BeginCount++;
6348 g.NextWindowData.ClearFlags();
6349
6350 // Update visibility
6351 if (first_begin_of_the_frame)
6352 {
6353 if (flags & ImGuiWindowFlags_ChildWindow)
6354 {
6355 // Child window can be out of sight and have "negative" clip windows.
6356 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6357 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6358 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow??
6359 if (!g.LogEnabled)
6360 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6361 window->HiddenFramesCanSkipItems = 1;
6362
6363 // Hide along with parent or if parent is collapsed
6364 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6365 window->HiddenFramesCanSkipItems = 1;
6366 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6367 window->HiddenFramesCannotSkipItems = 1;
6368 }
6369
6370 // 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)
6371 if (style.Alpha <= 0.0f)
6372 window->HiddenFramesCanSkipItems = 1;
6373
6374 // Update the Hidden flag
6375 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0);
6376
6377 // Disable inputs for requested number of frames
6378 if (window->DisableInputsFrames > 0)
6379 {
6380 window->DisableInputsFrames--;
6381 window->Flags |= ImGuiWindowFlags_NoInputs;
6382 }
6383
6384 // Update the SkipItems flag, used to early out of all items functions (no layout required)
6385 bool skip_items = false;
6386 if (window->Collapsed || !window->Active || window->Hidden)
6387 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6388 skip_items = true;
6389 window->SkipItems = skip_items;
6390 }
6391
6392 return !window->SkipItems;
6393}
6394
6395void ImGui::End()
6396{
6397 ImGuiContext& g = *GImGui;
6398 ImGuiWindow* window = g.CurrentWindow;
6399
6400 // Error checking: verify that user hasn't called End() too many times!
6401 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6402 {
6403 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6404 return;
6405 }
6406 IM_ASSERT(g.CurrentWindowStack.Size > 0);
6407
6408 // Error checking: verify that user doesn't directly call End() on a child window.
6409 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6410 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6411
6412 // Close anything that is open
6413 if (window->DC.CurrentColumns)
6414 EndColumns();
6415 PopClipRect(); // Inner window clip rectangle
6416
6417 // Stop logging
6418 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
6419 LogFinish();
6420
6421 // Pop from window stack
6422 g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;
6423 g.CurrentWindowStack.pop_back();
6424 if (window->Flags & ImGuiWindowFlags_Popup)
6425 g.BeginPopupStack.pop_back();
6426 window->DC.StackSizesOnBegin.CompareWithCurrentState();
6427 SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
6428}
6429
6430void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6431{
6432 ImGuiContext& g = *GImGui;
6433 IM_ASSERT(window == window->RootWindow);
6434
6435 const int cur_order = window->FocusOrder;
6436 IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
6437 if (g.WindowsFocusOrder.back() == window)
6438 return;
6439
6440 const int new_order = g.WindowsFocusOrder.Size - 1;
6441 for (int n = cur_order; n < new_order; n++)
6442 {
6443 g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
6444 g.WindowsFocusOrder[n]->FocusOrder--;
6445 IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
6446 }
6447 g.WindowsFocusOrder[new_order] = window;
6448 window->FocusOrder = (short)new_order;
6449}
6450
6451void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6452{
6453 ImGuiContext& g = *GImGui;
6454 ImGuiWindow* current_front_window = g.Windows.back();
6455 if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6456 return;
6457 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6458 if (g.Windows[i] == window)
6459 {
6460 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6461 g.Windows[g.Windows.Size - 1] = window;
6462 break;
6463 }
6464}
6465
6466void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6467{
6468 ImGuiContext& g = *GImGui;
6469 if (g.Windows[0] == window)
6470 return;
6471 for (int i = 0; i < g.Windows.Size; i++)
6472 if (g.Windows[i] == window)
6473 {
6474 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6475 g.Windows[0] = window;
6476 break;
6477 }
6478}
6479
6480// Moving window to front of display and set focus (which happens to be back of our sorted list)
6481void ImGui::FocusWindow(ImGuiWindow* window)
6482{
6483 ImGuiContext& g = *GImGui;
6484
6485 if (g.NavWindow != window)
6486 {
6487 g.NavWindow = window;
6488 if (window && g.NavDisableMouseHover)
6489 g.NavMousePosDirty = true;
6490 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6491 g.NavFocusScopeId = 0;
6492 g.NavIdIsAlive = false;
6493 g.NavLayer = ImGuiNavLayer_Main;
6494 g.NavInitRequest = g.NavMoveRequest = false;
6495 NavUpdateAnyRequestFlag();
6496 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6497 }
6498
6499 // Close popups if any
6500 ClosePopupsOverWindow(window, false);
6501
6502 // Move the root window to the top of the pile
6503 IM_ASSERT(window == NULL || window->RootWindow != NULL);
6504 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
6505 ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6506
6507 // Steal active widgets. Some of the cases it triggers includes:
6508 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6509 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6510 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6511 if (!g.ActiveIdNoClearOnFocusLoss)
6512 ClearActiveID();
6513
6514 // Passing NULL allow to disable keyboard focus
6515 if (!window)
6516 return;
6517
6518 // Bring to front
6519 BringWindowToFocusFront(focus_front_window);
6520 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6521 BringWindowToDisplayFront(display_front_window);
6522}
6523
6524void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6525{
6526 ImGuiContext& g = *GImGui;
6527
6528 const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1;
6529 for (int i = start_idx; i >= 0; i--)
6530 {
6531 // 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.
6532 ImGuiWindow* window = g.WindowsFocusOrder[i];
6533 IM_ASSERT(window == window->RootWindow);
6534 if (window != ignore_window && window->WasActive)
6535 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6536 {
6537 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6538 FocusWindow(focus_window);
6539 return;
6540 }
6541 }
6542 FocusWindow(NULL);
6543}
6544
6545// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.
6546void ImGui::SetCurrentFont(ImFont* font)
6547{
6548 ImGuiContext& g = *GImGui;
6549 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6550 IM_ASSERT(font->Scale > 0.0f);
6551 g.Font = font;
6552 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6553 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6554
6555 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6556 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6557 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6558 g.DrawListSharedData.Font = g.Font;
6559 g.DrawListSharedData.FontSize = g.FontSize;
6560}
6561
6562void ImGui::PushFont(ImFont* font)
6563{
6564 ImGuiContext& g = *GImGui;
6565 if (!font)
6566 font = GetDefaultFont();
6567 SetCurrentFont(font);
6568 g.FontStack.push_back(font);
6569 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6570}
6571
6572void ImGui::PopFont()
6573{
6574 ImGuiContext& g = *GImGui;
6575 g.CurrentWindow->DrawList->PopTextureID();
6576 g.FontStack.pop_back();
6577 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6578}
6579
6580void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6581{
6582 ImGuiContext& g = *GImGui;
6583 ImGuiItemFlags item_flags = g.CurrentItemFlags;
6584 IM_ASSERT(item_flags == g.ItemFlagsStack.back());
6585 if (enabled)
6586 item_flags |= option;
6587 else
6588 item_flags &= ~option;
6589 g.CurrentItemFlags = item_flags;
6590 g.ItemFlagsStack.push_back(item_flags);
6591}
6592
6593void ImGui::PopItemFlag()
6594{
6595 ImGuiContext& g = *GImGui;
6596 IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.
6597 g.ItemFlagsStack.pop_back();
6598 g.CurrentItemFlags = g.ItemFlagsStack.back();
6599}
6600
6601// PushDisabled()/PopDisabled()
6602// - Those can be nested but this cannot be used to enable an already disabled section (a single PushDisabled(true) in the stack is enough to keep things disabled)
6603// - Those are not yet exposed in imgui.h because we are unsure of how to alter the style in a way that works for everyone.
6604// We may rework this. Hypothetically, a future styling system may set a flag which make widgets use different colors.
6605// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
6606// - You may trivially implement your own variation of this if needed.
6607// Here we test (CurrentItemFlags & ImGuiItemFlags_Disabled) to allow nested PushDisabled() calls.
6608void ImGui::PushDisabled(bool disabled)
6609{
6610 ImGuiContext& g = *GImGui;
6611 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6612 if (!was_disabled && disabled)
6613 PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.6f);
6614 PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled);
6615}
6616
6617void ImGui::PopDisabled()
6618{
6619 ImGuiContext& g = *GImGui;
6620 bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
6621 PopItemFlag();
6622 if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
6623 PopStyleVar();
6624}
6625
6626// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
6627void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6628{
6629 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6630}
6631
6632void ImGui::PopAllowKeyboardFocus()
6633{
6634 PopItemFlag();
6635}
6636
6637void ImGui::PushButtonRepeat(bool repeat)
6638{
6639 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6640}
6641
6642void ImGui::PopButtonRepeat()
6643{
6644 PopItemFlag();
6645}
6646
6647void ImGui::PushTextWrapPos(float wrap_pos_x)
6648{
6649 ImGuiWindow* window = GetCurrentWindow();
6650 window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
6651 window->DC.TextWrapPos = wrap_pos_x;
6652}
6653
6654void ImGui::PopTextWrapPos()
6655{
6656 ImGuiWindow* window = GetCurrentWindow();
6657 window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
6658 window->DC.TextWrapPosStack.pop_back();
6659}
6660
6661bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6662{
6663 if (window->RootWindow == potential_parent)
6664 return true;
6665 while (window != NULL)
6666 {
6667 if (window == potential_parent)
6668 return true;
6669 window = window->ParentWindow;
6670 }
6671 return false;
6672}
6673
6674bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
6675{
6676 ImGuiContext& g = *GImGui;
6677 for (int i = g.Windows.Size - 1; i >= 0; i--)
6678 {
6679 ImGuiWindow* candidate_window = g.Windows[i];
6680 if (candidate_window == potential_above)
6681 return true;
6682 if (candidate_window == potential_below)
6683 return false;
6684 }
6685 return false;
6686}
6687
6688bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6689{
6690 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6691 ImGuiContext& g = *GImGui;
6692 if (g.HoveredWindow == NULL)
6693 return false;
6694
6695 if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
6696 {
6697 ImGuiWindow* window = g.CurrentWindow;
6698 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6699 {
6700 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6701 if (g.HoveredWindow->RootWindow != window->RootWindow)
6702 return false;
6703 break;
6704 case ImGuiHoveredFlags_RootWindow:
6705 if (g.HoveredWindow != window->RootWindow)
6706 return false;
6707 break;
6708 case ImGuiHoveredFlags_ChildWindows:
6709 if (!IsWindowChildOf(g.HoveredWindow, window))
6710 return false;
6711 break;
6712 default:
6713 if (g.HoveredWindow != window)
6714 return false;
6715 break;
6716 }
6717 }
6718
6719 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6720 return false;
6721 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6722 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6723 return false;
6724 return true;
6725}
6726
6727bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6728{
6729 ImGuiContext& g = *GImGui;
6730
6731 if (flags & ImGuiFocusedFlags_AnyWindow)
6732 return g.NavWindow != NULL;
6733
6734 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6735 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6736 {
6737 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6738 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6739 case ImGuiFocusedFlags_RootWindow:
6740 return g.NavWindow == g.CurrentWindow->RootWindow;
6741 case ImGuiFocusedFlags_ChildWindows:
6742 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6743 default:
6744 return g.NavWindow == g.CurrentWindow;
6745 }
6746}
6747
6748// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6749// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
6750// If you want a window to never be focused, you may use the e.g. NoInputs flag.
6751bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6752{
6753 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6754}
6755
6756float ImGui::GetWindowWidth()
6757{
6758 ImGuiWindow* window = GImGui->CurrentWindow;
6759 return window->Size.x;
6760}
6761
6762float ImGui::GetWindowHeight()
6763{
6764 ImGuiWindow* window = GImGui->CurrentWindow;
6765 return window->Size.y;
6766}
6767
6768ImVec2 ImGui::GetWindowPos()
6769{
6770 ImGuiContext& g = *GImGui;
6771 ImGuiWindow* window = g.CurrentWindow;
6772 return window->Pos;
6773}
6774
6775void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6776{
6777 // Test condition (NB: bit 0 is always true) and clear flags for next time
6778 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6779 return;
6780
6781 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6782 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6783 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6784
6785 // Set
6786 const ImVec2 old_pos = window->Pos;
6787 window->Pos = ImFloor(pos);
6788 ImVec2 offset = window->Pos - old_pos;
6789 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
6790 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6791 window->DC.IdealMaxPos += offset;
6792 window->DC.CursorStartPos += offset;
6793}
6794
6795void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6796{
6797 ImGuiWindow* window = GetCurrentWindowRead();
6798 SetWindowPos(window, pos, cond);
6799}
6800
6801void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6802{
6803 if (ImGuiWindow* window = FindWindowByName(name))
6804 SetWindowPos(window, pos, cond);
6805}
6806
6807ImVec2 ImGui::GetWindowSize()
6808{
6809 ImGuiWindow* window = GetCurrentWindowRead();
6810 return window->Size;
6811}
6812
6813void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6814{
6815 // Test condition (NB: bit 0 is always true) and clear flags for next time
6816 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6817 return;
6818
6819 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6820 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6821
6822 // Set
6823 if (size.x > 0.0f)
6824 {
6825 window->AutoFitFramesX = 0;
6826 window->SizeFull.x = IM_FLOOR(size.x);
6827 }
6828 else
6829 {
6830 window->AutoFitFramesX = 2;
6831 window->AutoFitOnlyGrows = false;
6832 }
6833 if (size.y > 0.0f)
6834 {
6835 window->AutoFitFramesY = 0;
6836 window->SizeFull.y = IM_FLOOR(size.y);
6837 }
6838 else
6839 {
6840 window->AutoFitFramesY = 2;
6841 window->AutoFitOnlyGrows = false;
6842 }
6843}
6844
6845void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6846{
6847 SetWindowSize(GImGui->CurrentWindow, size, cond);
6848}
6849
6850void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6851{
6852 if (ImGuiWindow* window = FindWindowByName(name))
6853 SetWindowSize(window, size, cond);
6854}
6855
6856void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6857{
6858 // Test condition (NB: bit 0 is always true) and clear flags for next time
6859 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6860 return;
6861 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6862
6863 // Set
6864 window->Collapsed = collapsed;
6865}
6866
6867void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
6868{
6869 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
6870 window->HitTestHoleSize = ImVec2ih(size);
6871 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
6872}
6873
6874void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6875{
6876 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6877}
6878
6879bool ImGui::IsWindowCollapsed()
6880{
6881 ImGuiWindow* window = GetCurrentWindowRead();
6882 return window->Collapsed;
6883}
6884
6885bool ImGui::IsWindowAppearing()
6886{
6887 ImGuiWindow* window = GetCurrentWindowRead();
6888 return window->Appearing;
6889}
6890
6891void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6892{
6893 if (ImGuiWindow* window = FindWindowByName(name))
6894 SetWindowCollapsed(window, collapsed, cond);
6895}
6896
6897void ImGui::SetWindowFocus()
6898{
6899 FocusWindow(GImGui->CurrentWindow);
6900}
6901
6902void ImGui::SetWindowFocus(const char* name)
6903{
6904 if (name)
6905 {
6906 if (ImGuiWindow* window = FindWindowByName(name))
6907 FocusWindow(window);
6908 }
6909 else
6910 {
6911 FocusWindow(NULL);
6912 }
6913}
6914
6915void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6916{
6917 ImGuiContext& g = *GImGui;
6918 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6919 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6920 g.NextWindowData.PosVal = pos;
6921 g.NextWindowData.PosPivotVal = pivot;
6922 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6923}
6924
6925void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6926{
6927 ImGuiContext& g = *GImGui;
6928 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6929 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6930 g.NextWindowData.SizeVal = size;
6931 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6932}
6933
6934void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6935{
6936 ImGuiContext& g = *GImGui;
6937 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6938 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6939 g.NextWindowData.SizeCallback = custom_callback;
6940 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6941}
6942
6943// Content size = inner scrollable rectangle, padded with WindowPadding.
6944// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
6945void ImGui::SetNextWindowContentSize(const ImVec2& size)
6946{
6947 ImGuiContext& g = *GImGui;
6948 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6949 g.NextWindowData.ContentSizeVal = ImFloor(size);
6950}
6951
6952void ImGui::SetNextWindowScroll(const ImVec2& scroll)
6953{
6954 ImGuiContext& g = *GImGui;
6955 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
6956 g.NextWindowData.ScrollVal = scroll;
6957}
6958
6959void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6960{
6961 ImGuiContext& g = *GImGui;
6962 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6963 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6964 g.NextWindowData.CollapsedVal = collapsed;
6965 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6966}
6967
6968void ImGui::SetNextWindowFocus()
6969{
6970 ImGuiContext& g = *GImGui;
6971 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6972}
6973
6974void ImGui::SetNextWindowBgAlpha(float alpha)
6975{
6976 ImGuiContext& g = *GImGui;
6977 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6978 g.NextWindowData.BgAlphaVal = alpha;
6979}
6980
6981ImDrawList* ImGui::GetWindowDrawList()
6982{
6983 ImGuiWindow* window = GetCurrentWindow();
6984 return window->DrawList;
6985}
6986
6987ImFont* ImGui::GetFont()
6988{
6989 return GImGui->Font;
6990}
6991
6992float ImGui::GetFontSize()
6993{
6994 return GImGui->FontSize;
6995}
6996
6997ImVec2 ImGui::GetFontTexUvWhitePixel()
6998{
6999 return GImGui->DrawListSharedData.TexUvWhitePixel;
7000}
7001
7002void ImGui::SetWindowFontScale(float scale)
7003{
7004 IM_ASSERT(scale > 0.0f);
7005 ImGuiContext& g = *GImGui;
7006 ImGuiWindow* window = GetCurrentWindow();
7007 window->FontWindowScale = scale;
7008 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
7009}
7010
7011void ImGui::ActivateItem(ImGuiID id)
7012{
7013 ImGuiContext& g = *GImGui;
7014 g.NavNextActivateId = id;
7015}
7016
7017void ImGui::PushFocusScope(ImGuiID id)
7018{
7019 ImGuiContext& g = *GImGui;
7020 ImGuiWindow* window = g.CurrentWindow;
7021 g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent);
7022 window->DC.NavFocusScopeIdCurrent = id;
7023}
7024
7025void ImGui::PopFocusScope()
7026{
7027 ImGuiContext& g = *GImGui;
7028 ImGuiWindow* window = g.CurrentWindow;
7029 IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?
7030 window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back();
7031 g.FocusScopeStack.pop_back();
7032}
7033
7034void ImGui::SetKeyboardFocusHere(int offset)
7035{
7036 IM_ASSERT(offset >= -1); // -1 is allowed but not below
7037 ImGuiContext& g = *GImGui;
7038 ImGuiWindow* window = g.CurrentWindow;
7039 g.TabFocusRequestNextWindow = window;
7040 g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
7041 g.TabFocusRequestNextCounterTabStop = INT_MAX;
7042}
7043
7044void ImGui::SetItemDefaultFocus()
7045{
7046 ImGuiContext& g = *GImGui;
7047 ImGuiWindow* window = g.CurrentWindow;
7048 if (!window->Appearing)
7049 return;
7050 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent)
7051 {
7052 g.NavInitRequest = false;
7053 g.NavInitResultId = g.LastItemData.ID;
7054 g.NavInitResultRectRel = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
7055 NavUpdateAnyRequestFlag();
7056 if (!IsItemVisible())
7057 SetScrollHereY();
7058 }
7059}
7060
7061void ImGui::SetStateStorage(ImGuiStorage* tree)
7062{
7063 ImGuiWindow* window = GImGui->CurrentWindow;
7064 window->DC.StateStorage = tree ? tree : &window->StateStorage;
7065}
7066
7067ImGuiStorage* ImGui::GetStateStorage()
7068{
7069 ImGuiWindow* window = GImGui->CurrentWindow;
7070 return window->DC.StateStorage;
7071}
7072
7073void ImGui::PushID(const char* str_id)
7074{
7075 ImGuiContext& g = *GImGui;
7076 ImGuiWindow* window = g.CurrentWindow;
7077 ImGuiID id = window->GetIDNoKeepAlive(str_id);
7078 window->IDStack.push_back(id);
7079}
7080
7081void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
7082{
7083 ImGuiContext& g = *GImGui;
7084 ImGuiWindow* window = g.CurrentWindow;
7085 ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
7086 window->IDStack.push_back(id);
7087}
7088
7089void ImGui::PushID(const void* ptr_id)
7090{
7091 ImGuiContext& g = *GImGui;
7092 ImGuiWindow* window = g.CurrentWindow;
7093 ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
7094 window->IDStack.push_back(id);
7095}
7096
7097void ImGui::PushID(int int_id)
7098{
7099 ImGuiContext& g = *GImGui;
7100 ImGuiWindow* window = g.CurrentWindow;
7101 ImGuiID id = window->GetIDNoKeepAlive(int_id);
7102 window->IDStack.push_back(id);
7103}
7104
7105// Push a given id value ignoring the ID stack as a seed.
7106void ImGui::PushOverrideID(ImGuiID id)
7107{
7108 ImGuiContext& g = *GImGui;
7109 ImGuiWindow* window = g.CurrentWindow;
7110 window->IDStack.push_back(id);
7111}
7112
7113// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
7114// (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level.
7115// for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
7116ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
7117{
7118 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
7119 ImGui::KeepAliveID(id);
7120#ifdef IMGUI_ENABLE_TEST_ENGINE
7121 ImGuiContext& g = *GImGui;
7122 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
7123#endif
7124 return id;
7125}
7126
7127void ImGui::PopID()
7128{
7129 ImGuiWindow* window = GImGui->CurrentWindow;
7130 IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
7131 window->IDStack.pop_back();
7132}
7133
7134ImGuiID ImGui::GetID(const char* str_id)
7135{
7136 ImGuiWindow* window = GImGui->CurrentWindow;
7137 return window->GetID(str_id);
7138}
7139
7140ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
7141{
7142 ImGuiWindow* window = GImGui->CurrentWindow;
7143 return window->GetID(str_id_begin, str_id_end);
7144}
7145
7146ImGuiID ImGui::GetID(const void* ptr_id)
7147{
7148 ImGuiWindow* window = GImGui->CurrentWindow;
7149 return window->GetID(ptr_id);
7150}
7151
7152bool ImGui::IsRectVisible(const ImVec2& size)
7153{
7154 ImGuiWindow* window = GImGui->CurrentWindow;
7155 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7156}
7157
7158bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7159{
7160 ImGuiWindow* window = GImGui->CurrentWindow;
7161 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7162}
7163
7164
7165//-----------------------------------------------------------------------------
7166// [SECTION] ERROR CHECKING
7167//-----------------------------------------------------------------------------
7168
7169// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
7170// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
7171// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
7172// may see different structures than what imgui.cpp sees, which is problematic.
7173// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui.
7174bool 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)
7175{
7176 bool error = false;
7177 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
7178 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
7179 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
7180 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
7181 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
7182 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
7183 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
7184 return !error;
7185}
7186
7187static void ImGui::ErrorCheckNewFrameSanityChecks()
7188{
7189 ImGuiContext& g = *GImGui;
7190
7191 // Check user IM_ASSERT macro
7192 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
7193 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
7194 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
7195 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
7196 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
7197 if (true) IM_ASSERT(1); else IM_ASSERT(0);
7198
7199 // Check user data
7200 // (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)
7201 IM_ASSERT(g.Initialized);
7202 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
7203 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
7204 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
7205 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()");
7206 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
7207 IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!");
7208 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
7209 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
7210 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
7211 for (int n = 0; n < ImGuiKey_COUNT; n++)
7212 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)");
7213
7214 // 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)
7215 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
7216 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
7217
7218 // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
7219 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
7220 g.IO.ConfigWindowsResizeFromEdges = false;
7221}
7222
7223static void ImGui::ErrorCheckEndFrameSanityChecks()
7224{
7225 ImGuiContext& g = *GImGui;
7226
7227 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
7228 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
7229 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
7230 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
7231 // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
7232 // while still correctly asserting on mid-frame key press events.
7233 const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags();
7234 IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
7235 IM_UNUSED(key_mod_flags);
7236
7237 // Recover from errors
7238 //ErrorCheckEndFrameRecover();
7239
7240 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7241 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7242 if (g.CurrentWindowStack.Size != 1)
7243 {
7244 if (g.CurrentWindowStack.Size > 1)
7245 {
7246 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7247 while (g.CurrentWindowStack.Size > 1)
7248 End();
7249 }
7250 else
7251 {
7252 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7253 }
7254 }
7255
7256 IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
7257}
7258
7259// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
7260// Must be called during or before EndFrame().
7261// This is generally flawed as we are not necessarily End/Popping things in the right order.
7262// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
7263// FIXME: Can't recover from interleaved BeginTabBar/Begin
7264void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
7265{
7266 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
7267 ImGuiContext& g = *GImGui;
7268 while (g.CurrentWindowStack.Size > 0)
7269 {
7270 while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
7271 {
7272 if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
7273 EndTable();
7274 }
7275 ImGuiWindow* window = g.CurrentWindow;
7276 IM_ASSERT(window != NULL);
7277 while (g.CurrentTabBar != NULL) //-V1044
7278 {
7279 if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
7280 EndTabBar();
7281 }
7282 while (window->DC.TreeDepth > 0)
7283 {
7284 if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
7285 TreePop();
7286 }
7287 while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack)
7288 {
7289 if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
7290 EndGroup();
7291 }
7292 while (window->IDStack.Size > 1)
7293 {
7294 if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
7295 PopID();
7296 }
7297 while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack)
7298 {
7299 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
7300 PopStyleColor();
7301 }
7302 while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack)
7303 {
7304 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
7305 PopStyleVar();
7306 }
7307 while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack)
7308 {
7309 if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
7310 PopFocusScope();
7311 }
7312 if (g.CurrentWindowStack.Size == 1)
7313 {
7314 IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
7315 break;
7316 }
7317 IM_ASSERT(window == g.CurrentWindow);
7318 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7319 {
7320 if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
7321 EndChild();
7322 }
7323 else
7324 {
7325 if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
7326 End();
7327 }
7328 }
7329}
7330
7331// Save current stack sizes for later compare
7332void ImGuiStackSizes::SetToCurrentState()
7333{
7334 ImGuiContext& g = *GImGui;
7335 ImGuiWindow* window = g.CurrentWindow;
7336 SizeOfIDStack = (short)window->IDStack.Size;
7337 SizeOfColorStack = (short)g.ColorStack.Size;
7338 SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
7339 SizeOfFontStack = (short)g.FontStack.Size;
7340 SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
7341 SizeOfGroupStack = (short)g.GroupStack.Size;
7342 SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
7343}
7344
7345// Compare to detect usage errors
7346void ImGuiStackSizes::CompareWithCurrentState()
7347{
7348 ImGuiContext& g = *GImGui;
7349 ImGuiWindow* window = g.CurrentWindow;
7350 IM_UNUSED(window);
7351
7352 // Window stacks
7353 // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
7354 IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!");
7355
7356 // Global stacks
7357 // 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.
7358 IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!");
7359 IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!");
7360 IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!");
7361 IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!");
7362 IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!");
7363 IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!");
7364}
7365
7366
7367//-----------------------------------------------------------------------------
7368// [SECTION] LAYOUT
7369//-----------------------------------------------------------------------------
7370// - ItemSize()
7371// - ItemAdd()
7372// - SameLine()
7373// - GetCursorScreenPos()
7374// - SetCursorScreenPos()
7375// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
7376// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
7377// - GetCursorStartPos()
7378// - Indent()
7379// - Unindent()
7380// - SetNextItemWidth()
7381// - PushItemWidth()
7382// - PushMultiItemsWidths()
7383// - PopItemWidth()
7384// - CalcItemWidth()
7385// - CalcItemSize()
7386// - GetTextLineHeight()
7387// - GetTextLineHeightWithSpacing()
7388// - GetFrameHeight()
7389// - GetFrameHeightWithSpacing()
7390// - GetContentRegionMax()
7391// - GetContentRegionMaxAbs() [Internal]
7392// - GetContentRegionAvail(),
7393// - GetWindowContentRegionMin(), GetWindowContentRegionMax()
7394// - GetWindowContentRegionWidth()
7395// - BeginGroup()
7396// - EndGroup()
7397// Also see in imgui_widgets: tab bars, columns.
7398//-----------------------------------------------------------------------------
7399
7400// Advance cursor given item size for layout.
7401// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
7402// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
7403void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
7404{
7405 ImGuiContext& g = *GImGui;
7406 ImGuiWindow* window = g.CurrentWindow;
7407 if (window->SkipItems)
7408 return;
7409
7410 // We increase the height in this function to accommodate for baseline offset.
7411 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
7412 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
7413 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
7414 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
7415
7416 // Always align ourselves on pixel boundaries
7417 //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]
7418 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
7419 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
7420 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
7421 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
7422 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
7423 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
7424 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
7425
7426 window->DC.PrevLineSize.y = line_height;
7427 window->DC.CurrLineSize.y = 0.0f;
7428 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
7429 window->DC.CurrLineTextBaseOffset = 0.0f;
7430
7431 // Horizontal layout mode
7432 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
7433 SameLine();
7434}
7435
7436void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
7437{
7438 ItemSize(bb.GetSize(), text_baseline_y);
7439}
7440
7441// Declare item bounding box for clipping and interaction.
7442// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
7443// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
7444bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags)
7445{
7446 ImGuiContext& g = *GImGui;
7447 ImGuiWindow* window = g.CurrentWindow;
7448
7449 // Set item data
7450 g.LastItemData.ID = id;
7451 g.LastItemData.Rect = bb;
7452 g.LastItemData.InFlags = g.CurrentItemFlags;
7453 g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
7454
7455 // Directional navigation processing
7456 if (id != 0)
7457 {
7458 // Runs prior to clipping early-out
7459 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
7460 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
7461 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
7462 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
7463 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
7464 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
7465 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
7466 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
7467 window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
7468 if (g.NavId == id || g.NavAnyRequest)
7469 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
7470 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
7471 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
7472
7473 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
7474#ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
7475 if (id == g.DebugItemPickerBreakId)
7476 {
7477 IM_DEBUG_BREAK();
7478 g.DebugItemPickerBreakId = 0;
7479 }
7480#endif
7481 }
7482 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7483
7484#ifdef IMGUI_ENABLE_TEST_ENGINE
7485 if (id != 0)
7486 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7487#endif
7488
7489 // Clipping test
7490 const bool is_clipped = IsClippedEx(bb, id, false);
7491 if (is_clipped)
7492 return false;
7493 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7494
7495 // Tab stop handling (previously was using internal ItemFocusable() api)
7496 // 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)
7497 if (flags & ImGuiItemAddFlags_Focusable)
7498 ItemFocusable(window, id);
7499
7500 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7501 if (IsMouseHoveringRect(bb.Min, bb.Max))
7502 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7503 return true;
7504}
7505
7506// Gets back to previous line and continue with horizontal layout
7507// offset_from_start_x == 0 : follow right after previous item
7508// offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7509// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
7510// spacing_w >= 0 : enforce spacing amount
7511void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7512{
7513 ImGuiWindow* window = GetCurrentWindow();
7514 if (window->SkipItems)
7515 return;
7516
7517 ImGuiContext& g = *GImGui;
7518 if (offset_from_start_x != 0.0f)
7519 {
7520 if (spacing_w < 0.0f) spacing_w = 0.0f;
7521 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7522 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7523 }
7524 else
7525 {
7526 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7527 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7528 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7529 }
7530 window->DC.CurrLineSize = window->DC.PrevLineSize;
7531 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7532}
7533
7534ImVec2 ImGui::GetCursorScreenPos()
7535{
7536 ImGuiWindow* window = GetCurrentWindowRead();
7537 return window->DC.CursorPos;
7538}
7539
7540void ImGui::SetCursorScreenPos(const ImVec2& pos)
7541{
7542 ImGuiWindow* window = GetCurrentWindow();
7543 window->DC.CursorPos = pos;
7544 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7545}
7546
7547// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7548// 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'.
7549ImVec2 ImGui::GetCursorPos()
7550{
7551 ImGuiWindow* window = GetCurrentWindowRead();
7552 return window->DC.CursorPos - window->Pos + window->Scroll;
7553}
7554
7555float ImGui::GetCursorPosX()
7556{
7557 ImGuiWindow* window = GetCurrentWindowRead();
7558 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7559}
7560
7561float ImGui::GetCursorPosY()
7562{
7563 ImGuiWindow* window = GetCurrentWindowRead();
7564 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7565}
7566
7567void ImGui::SetCursorPos(const ImVec2& local_pos)
7568{
7569 ImGuiWindow* window = GetCurrentWindow();
7570 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7571 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7572}
7573
7574void ImGui::SetCursorPosX(float x)
7575{
7576 ImGuiWindow* window = GetCurrentWindow();
7577 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7578 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7579}
7580
7581void ImGui::SetCursorPosY(float y)
7582{
7583 ImGuiWindow* window = GetCurrentWindow();
7584 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7585 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7586}
7587
7588ImVec2 ImGui::GetCursorStartPos()
7589{
7590 ImGuiWindow* window = GetCurrentWindowRead();
7591 return window->DC.CursorStartPos - window->Pos;
7592}
7593
7594void ImGui::Indent(float indent_w)
7595{
7596 ImGuiContext& g = *GImGui;
7597 ImGuiWindow* window = GetCurrentWindow();
7598 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7599 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7600}
7601
7602void ImGui::Unindent(float indent_w)
7603{
7604 ImGuiContext& g = *GImGui;
7605 ImGuiWindow* window = GetCurrentWindow();
7606 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7607 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7608}
7609
7610// Affect large frame+labels widgets only.
7611void ImGui::SetNextItemWidth(float item_width)
7612{
7613 ImGuiContext& g = *GImGui;
7614 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7615 g.NextItemData.Width = item_width;
7616}
7617
7618// FIXME: Remove the == 0.0f behavior?
7619void ImGui::PushItemWidth(float item_width)
7620{
7621 ImGuiContext& g = *GImGui;
7622 ImGuiWindow* window = g.CurrentWindow;
7623 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
7624 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7625 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7626}
7627
7628void ImGui::PushMultiItemsWidths(int components, float w_full)
7629{
7630 ImGuiContext& g = *GImGui;
7631 ImGuiWindow* window = g.CurrentWindow;
7632 const ImGuiStyle& style = g.Style;
7633 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7634 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7635 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
7636 window->DC.ItemWidthStack.push_back(w_item_last);
7637 for (int i = 0; i < components - 2; i++)
7638 window->DC.ItemWidthStack.push_back(w_item_one);
7639 window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one;
7640 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7641}
7642
7643void ImGui::PopItemWidth()
7644{
7645 ImGuiWindow* window = GetCurrentWindow();
7646 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7647 window->DC.ItemWidthStack.pop_back();
7648}
7649
7650// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7651// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
7652float ImGui::CalcItemWidth()
7653{
7654 ImGuiContext& g = *GImGui;
7655 ImGuiWindow* window = g.CurrentWindow;
7656 float w;
7657 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7658 w = g.NextItemData.Width;
7659 else
7660 w = window->DC.ItemWidth;
7661 if (w < 0.0f)
7662 {
7663 float region_max_x = GetContentRegionMaxAbs().x;
7664 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7665 }
7666 w = IM_FLOOR(w);
7667 return w;
7668}
7669
7670// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7671// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7672// Note that only CalcItemWidth() is publicly exposed.
7673// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
7674ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7675{
7676 ImGuiWindow* window = GImGui->CurrentWindow;
7677
7678 ImVec2 region_max;
7679 if (size.x < 0.0f || size.y < 0.0f)
7680 region_max = GetContentRegionMaxAbs();
7681
7682 if (size.x == 0.0f)
7683 size.x = default_w;
7684 else if (size.x < 0.0f)
7685 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7686
7687 if (size.y == 0.0f)
7688 size.y = default_h;
7689 else if (size.y < 0.0f)
7690 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7691
7692 return size;
7693}
7694
7695float ImGui::GetTextLineHeight()
7696{
7697 ImGuiContext& g = *GImGui;
7698 return g.FontSize;
7699}
7700
7701float ImGui::GetTextLineHeightWithSpacing()
7702{
7703 ImGuiContext& g = *GImGui;
7704 return g.FontSize + g.Style.ItemSpacing.y;
7705}
7706
7707float ImGui::GetFrameHeight()
7708{
7709 ImGuiContext& g = *GImGui;
7710 return g.FontSize + g.Style.FramePadding.y * 2.0f;
7711}
7712
7713float ImGui::GetFrameHeightWithSpacing()
7714{
7715 ImGuiContext& g = *GImGui;
7716 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7717}
7718
7719// 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!
7720
7721// FIXME: This is in window space (not screen space!).
7722ImVec2 ImGui::GetContentRegionMax()
7723{
7724 ImGuiContext& g = *GImGui;
7725 ImGuiWindow* window = g.CurrentWindow;
7726 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7727 if (window->DC.CurrentColumns || g.CurrentTable)
7728 mx.x = window->WorkRect.Max.x - window->Pos.x;
7729 return mx;
7730}
7731
7732// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
7733ImVec2 ImGui::GetContentRegionMaxAbs()
7734{
7735 ImGuiContext& g = *GImGui;
7736 ImGuiWindow* window = g.CurrentWindow;
7737 ImVec2 mx = window->ContentRegionRect.Max;
7738 if (window->DC.CurrentColumns || g.CurrentTable)
7739 mx.x = window->WorkRect.Max.x;
7740 return mx;
7741}
7742
7743ImVec2 ImGui::GetContentRegionAvail()
7744{
7745 ImGuiWindow* window = GImGui->CurrentWindow;
7746 return GetContentRegionMaxAbs() - window->DC.CursorPos;
7747}
7748
7749// In window space (not screen space!)
7750ImVec2 ImGui::GetWindowContentRegionMin()
7751{
7752 ImGuiWindow* window = GImGui->CurrentWindow;
7753 return window->ContentRegionRect.Min - window->Pos;
7754}
7755
7756ImVec2 ImGui::GetWindowContentRegionMax()
7757{
7758 ImGuiWindow* window = GImGui->CurrentWindow;
7759 return window->ContentRegionRect.Max - window->Pos;
7760}
7761
7762float ImGui::GetWindowContentRegionWidth()
7763{
7764 ImGuiWindow* window = GImGui->CurrentWindow;
7765 return window->ContentRegionRect.GetWidth();
7766}
7767
7768// 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.)
7769// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
7770void ImGui::BeginGroup()
7771{
7772 ImGuiContext& g = *GImGui;
7773 ImGuiWindow* window = g.CurrentWindow;
7774
7775 g.GroupStack.resize(g.GroupStack.Size + 1);
7776 ImGuiGroupData& group_data = g.GroupStack.back();
7777 group_data.WindowID = window->ID;
7778 group_data.BackupCursorPos = window->DC.CursorPos;
7779 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7780 group_data.BackupIndent = window->DC.Indent;
7781 group_data.BackupGroupOffset = window->DC.GroupOffset;
7782 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7783 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7784 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7785 group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
7786 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7787 group_data.EmitItem = true;
7788
7789 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7790 window->DC.Indent = window->DC.GroupOffset;
7791 window->DC.CursorMaxPos = window->DC.CursorPos;
7792 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7793 if (g.LogEnabled)
7794 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
7795}
7796
7797void ImGui::EndGroup()
7798{
7799 ImGuiContext& g = *GImGui;
7800 ImGuiWindow* window = g.CurrentWindow;
7801 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
7802
7803 ImGuiGroupData& group_data = g.GroupStack.back();
7804 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
7805
7806 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7807
7808 window->DC.CursorPos = group_data.BackupCursorPos;
7809 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7810 window->DC.Indent = group_data.BackupIndent;
7811 window->DC.GroupOffset = group_data.BackupGroupOffset;
7812 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7813 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7814 if (g.LogEnabled)
7815 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
7816
7817 if (!group_data.EmitItem)
7818 {
7819 g.GroupStack.pop_back();
7820 return;
7821 }
7822
7823 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.
7824 ItemSize(group_bb.GetSize());
7825 ItemAdd(group_bb, 0);
7826
7827 // 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.
7828 // 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.
7829 // Also if you grep for LastItemId you'll notice it is only used in that context.
7830 // (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.)
7831 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7832 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
7833 if (group_contains_curr_active_id)
7834 g.LastItemData.ID = g.ActiveId;
7835 else if (group_contains_prev_active_id)
7836 g.LastItemData.ID = g.ActiveIdPreviousFrame;
7837 g.LastItemData.Rect = group_bb;
7838
7839 // Forward Hovered flag
7840 const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
7841 if (group_contains_curr_hovered_id)
7842 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
7843
7844 // Forward Edited flag
7845 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7846 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
7847
7848 // Forward Deactivated flag
7849 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7850 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7851 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
7852
7853 g.GroupStack.pop_back();
7854 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
7855}
7856
7857
7858//-----------------------------------------------------------------------------
7859// [SECTION] SCROLLING
7860//-----------------------------------------------------------------------------
7861
7862// Helper to snap on edges when aiming at an item very close to the edge,
7863// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
7864// When we refactor the scrolling API this may be configurable with a flag?
7865// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
7866static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
7867{
7868 if (target <= snap_min + snap_threshold)
7869 return ImLerp(snap_min, target, center_ratio);
7870 if (target >= snap_max - snap_threshold)
7871 return ImLerp(target, snap_max, center_ratio);
7872 return target;
7873}
7874
7875static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
7876{
7877 ImVec2 scroll = window->Scroll;
7878 if (window->ScrollTarget.x < FLT_MAX)
7879 {
7880 float decoration_total_width = window->ScrollbarSizes.x;
7881 float center_x_ratio = window->ScrollTargetCenterRatio.x;
7882 float scroll_target_x = window->ScrollTarget.x;
7883 if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
7884 {
7885 float snap_x_min = 0.0f;
7886 float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width;
7887 scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
7888 }
7889 scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width);
7890 }
7891 if (window->ScrollTarget.y < FLT_MAX)
7892 {
7893 float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y;
7894 float center_y_ratio = window->ScrollTargetCenterRatio.y;
7895 float scroll_target_y = window->ScrollTarget.y;
7896 if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
7897 {
7898 float snap_y_min = 0.0f;
7899 float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height;
7900 scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
7901 }
7902 scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height);
7903 }
7904 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
7905 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
7906 if (!window->Collapsed && !window->SkipItems)
7907 {
7908 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7909 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7910 }
7911 return scroll;
7912}
7913
7914// Scroll to keep newly navigated item fully into view
7915ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7916{
7917 ImGuiContext& g = *GImGui;
7918 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7919 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7920
7921 ImVec2 delta_scroll;
7922 if (!window_rect.Contains(item_rect))
7923 {
7924 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7925 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
7926 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7927 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7928 if (item_rect.Min.y < window_rect.Min.y)
7929 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7930 else if (item_rect.Max.y >= window_rect.Max.y)
7931 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7932
7933 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7934 delta_scroll = next_scroll - window->Scroll;
7935 }
7936
7937 // Also scroll parent window to keep us into view if necessary
7938 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7939 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7940
7941 return delta_scroll;
7942}
7943
7944float ImGui::GetScrollX()
7945{
7946 ImGuiWindow* window = GImGui->CurrentWindow;
7947 return window->Scroll.x;
7948}
7949
7950float ImGui::GetScrollY()
7951{
7952 ImGuiWindow* window = GImGui->CurrentWindow;
7953 return window->Scroll.y;
7954}
7955
7956float ImGui::GetScrollMaxX()
7957{
7958 ImGuiWindow* window = GImGui->CurrentWindow;
7959 return window->ScrollMax.x;
7960}
7961
7962float ImGui::GetScrollMaxY()
7963{
7964 ImGuiWindow* window = GImGui->CurrentWindow;
7965 return window->ScrollMax.y;
7966}
7967
7968void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
7969{
7970 window->ScrollTarget.x = scroll_x;
7971 window->ScrollTargetCenterRatio.x = 0.0f;
7972 window->ScrollTargetEdgeSnapDist.x = 0.0f;
7973}
7974
7975void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
7976{
7977 window->ScrollTarget.y = scroll_y;
7978 window->ScrollTargetCenterRatio.y = 0.0f;
7979 window->ScrollTargetEdgeSnapDist.y = 0.0f;
7980}
7981
7982void ImGui::SetScrollX(float scroll_x)
7983{
7984 ImGuiContext& g = *GImGui;
7985 SetScrollX(g.CurrentWindow, scroll_x);
7986}
7987
7988void ImGui::SetScrollY(float scroll_y)
7989{
7990 ImGuiContext& g = *GImGui;
7991 SetScrollY(g.CurrentWindow, scroll_y);
7992}
7993
7994// Note that a local position will vary depending on initial scroll value,
7995// This is a little bit confusing so bear with us:
7996// - local_pos = (absolution_pos - window->Pos)
7997// - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
7998// and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
7999// - They mostly exists because of legacy API.
8000// Following the rules above, when trying to work with scrolling code, consider that:
8001// - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
8002// - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
8003// 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
8004void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
8005{
8006 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
8007 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
8008 window->ScrollTargetCenterRatio.x = center_x_ratio;
8009 window->ScrollTargetEdgeSnapDist.x = 0.0f;
8010}
8011
8012void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
8013{
8014 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
8015 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect;
8016 local_y -= decoration_up_height;
8017 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
8018 window->ScrollTargetCenterRatio.y = center_y_ratio;
8019 window->ScrollTargetEdgeSnapDist.y = 0.0f;
8020}
8021
8022void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
8023{
8024 ImGuiContext& g = *GImGui;
8025 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
8026}
8027
8028void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
8029{
8030 ImGuiContext& g = *GImGui;
8031 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
8032}
8033
8034// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
8035void ImGui::SetScrollHereX(float center_x_ratio)
8036{
8037 ImGuiContext& g = *GImGui;
8038 ImGuiWindow* window = g.CurrentWindow;
8039 float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
8040 float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
8041 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
8042
8043 // Tweak: snap on edges when aiming at an item very close to the edge
8044 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
8045}
8046
8047// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
8048void ImGui::SetScrollHereY(float center_y_ratio)
8049{
8050 ImGuiContext& g = *GImGui;
8051 ImGuiWindow* window = g.CurrentWindow;
8052 float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
8053 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
8054 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
8055
8056 // Tweak: snap on edges when aiming at an item very close to the edge
8057 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
8058}
8059
8060//-----------------------------------------------------------------------------
8061// [SECTION] TOOLTIPS
8062//-----------------------------------------------------------------------------
8063
8064void ImGui::BeginTooltip()
8065{
8066 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
8067}
8068
8069void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
8070{
8071 ImGuiContext& g = *GImGui;
8072
8073 if (g.DragDropWithinSource || g.DragDropWithinTarget)
8074 {
8075 // 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)
8076 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
8077 // 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.
8078 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
8079 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
8080 SetNextWindowPos(tooltip_pos);
8081 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
8082 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
8083 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
8084 }
8085
8086 char window_name[16];
8087 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
8088 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
8089 if (ImGuiWindow* window = FindWindowByName(window_name))
8090 if (window->Active)
8091 {
8092 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
8093 window->Hidden = true;
8094 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary?
8095 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
8096 }
8097 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
8098 Begin(window_name, NULL, flags | extra_flags);
8099}
8100
8101void ImGui::EndTooltip()
8102{
8103 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
8104 End();
8105}
8106
8107void ImGui::SetTooltipV(const char* fmt, va_list args)
8108{
8109 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
8110 TextV(fmt, args);
8111 EndTooltip();
8112}
8113
8114void ImGui::SetTooltip(const char* fmt, ...)
8115{
8116 va_list args;
8117 va_start(args, fmt);
8118 SetTooltipV(fmt, args);
8119 va_end(args);
8120}
8121
8122//-----------------------------------------------------------------------------
8123// [SECTION] POPUPS
8124//-----------------------------------------------------------------------------
8125
8126// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
8127bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
8128{
8129 ImGuiContext& g = *GImGui;
8130 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
8131 {
8132 // Return true if any popup is open at the current BeginPopup() level of the popup stack
8133 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
8134 IM_ASSERT(id == 0);
8135 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8136 return g.OpenPopupStack.Size > 0;
8137 else
8138 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
8139 }
8140 else
8141 {
8142 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
8143 {
8144 // Return true if the popup is open anywhere in the popup stack
8145 for (int n = 0; n < g.OpenPopupStack.Size; n++)
8146 if (g.OpenPopupStack[n].PopupId == id)
8147 return true;
8148 return false;
8149 }
8150 else
8151 {
8152 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
8153 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
8154 }
8155 }
8156}
8157
8158bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
8159{
8160 ImGuiContext& g = *GImGui;
8161 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
8162 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
8163 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
8164 return IsPopupOpen(id, popup_flags);
8165}
8166
8167ImGuiWindow* ImGui::GetTopMostPopupModal()
8168{
8169 ImGuiContext& g = *GImGui;
8170 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
8171 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
8172 if (popup->Flags & ImGuiWindowFlags_Modal)
8173 return popup;
8174 return NULL;
8175}
8176
8177void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
8178{
8179 ImGuiContext& g = *GImGui;
8180 OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
8181}
8182
8183void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
8184{
8185 OpenPopupEx(id, popup_flags);
8186}
8187
8188// Mark popup as open (toggle toward open state).
8189// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
8190// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
8191// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
8192void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
8193{
8194 ImGuiContext& g = *GImGui;
8195 ImGuiWindow* parent_window = g.CurrentWindow;
8196 const int current_stack_size = g.BeginPopupStack.Size;
8197
8198 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
8199 if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
8200 return;
8201
8202 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
8203 popup_ref.PopupId = id;
8204 popup_ref.Window = NULL;
8205 popup_ref.SourceWindow = g.NavWindow;
8206 popup_ref.OpenFrameCount = g.FrameCount;
8207 popup_ref.OpenParentId = parent_window->IDStack.back();
8208 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
8209 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
8210
8211 IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
8212 if (g.OpenPopupStack.Size < current_stack_size + 1)
8213 {
8214 g.OpenPopupStack.push_back(popup_ref);
8215 }
8216 else
8217 {
8218 // 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
8219 // 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
8220 // 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.
8221 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
8222 {
8223 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
8224 }
8225 else
8226 {
8227 // Close child popups if any, then flag popup for open/reopen
8228 ClosePopupToLevel(current_stack_size, false);
8229 g.OpenPopupStack.push_back(popup_ref);
8230 }
8231
8232 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
8233 // This is equivalent to what ClosePopupToLevel() does.
8234 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
8235 // FocusWindow(parent_window);
8236 }
8237}
8238
8239// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
8240// This function closes any popups that are over 'ref_window'.
8241void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
8242{
8243 ImGuiContext& g = *GImGui;
8244 if (g.OpenPopupStack.Size == 0)
8245 return;
8246
8247 // Don't close our own child popup windows.
8248 int popup_count_to_keep = 0;
8249 if (ref_window)
8250 {
8251 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
8252 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
8253 {
8254 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
8255 if (!popup.Window)
8256 continue;
8257 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
8258 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
8259 continue;
8260
8261 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
8262 // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
8263 // Window -> Popup1 -> Popup2 -> Popup3
8264 // - Each popups may contain child windows, which is why we compare ->RootWindow!
8265 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
8266 bool ref_window_is_descendent_of_popup = false;
8267 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
8268 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
8269 if (popup_window->RootWindow == ref_window->RootWindow)
8270 {
8271 ref_window_is_descendent_of_popup = true;
8272 break;
8273 }
8274 if (!ref_window_is_descendent_of_popup)
8275 break;
8276 }
8277 }
8278 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
8279 {
8280 IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
8281 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
8282 }
8283}
8284
8285void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
8286{
8287 ImGuiContext& g = *GImGui;
8288 IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
8289 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
8290
8291 // Trim open popup stack
8292 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
8293 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
8294 g.OpenPopupStack.resize(remaining);
8295
8296 if (restore_focus_to_window_under_popup)
8297 {
8298 if (focus_window && !focus_window->WasActive && popup_window)
8299 {
8300 // Fallback
8301 FocusTopMostWindowUnderOne(popup_window, NULL);
8302 }
8303 else
8304 {
8305 if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
8306 focus_window = NavRestoreLastChildNavWindow(focus_window);
8307 FocusWindow(focus_window);
8308 }
8309 }
8310}
8311
8312// Close the popup we have begin-ed into.
8313void ImGui::CloseCurrentPopup()
8314{
8315 ImGuiContext& g = *GImGui;
8316 int popup_idx = g.BeginPopupStack.Size - 1;
8317 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
8318 return;
8319
8320 // Closing a menu closes its top-most parent popup (unless a modal)
8321 while (popup_idx > 0)
8322 {
8323 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
8324 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
8325 bool close_parent = false;
8326 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
8327 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
8328 close_parent = true;
8329 if (!close_parent)
8330 break;
8331 popup_idx--;
8332 }
8333 IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
8334 ClosePopupToLevel(popup_idx, true);
8335
8336 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
8337 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
8338 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
8339 if (ImGuiWindow* window = g.NavWindow)
8340 window->DC.NavHideHighlightOneFrame = true;
8341}
8342
8343// Attention! BeginPopup() adds default flags which BeginPopupEx()!
8344bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
8345{
8346 ImGuiContext& g = *GImGui;
8347 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8348 {
8349 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8350 return false;
8351 }
8352
8353 char name[20];
8354 if (flags & ImGuiWindowFlags_ChildMenu)
8355 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
8356 else
8357 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
8358
8359 flags |= ImGuiWindowFlags_Popup;
8360 bool is_open = Begin(name, NULL, flags);
8361 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
8362 EndPopup();
8363
8364 return is_open;
8365}
8366
8367bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
8368{
8369 ImGuiContext& g = *GImGui;
8370 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
8371 {
8372 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8373 return false;
8374 }
8375 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
8376 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
8377}
8378
8379// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
8380// 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.
8381bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
8382{
8383 ImGuiContext& g = *GImGui;
8384 ImGuiWindow* window = g.CurrentWindow;
8385 const ImGuiID id = window->GetID(name);
8386 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
8387 {
8388 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8389 return false;
8390 }
8391
8392 // Center modal windows by default for increased visibility
8393 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
8394 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
8395 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
8396 {
8397 const ImGuiViewport* viewport = GetMainViewport();
8398 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
8399 }
8400
8401 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
8402 const bool is_open = Begin(name, p_open, flags);
8403 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
8404 {
8405 EndPopup();
8406 if (is_open)
8407 ClosePopupToLevel(g.BeginPopupStack.Size, true);
8408 return false;
8409 }
8410 return is_open;
8411}
8412
8413void ImGui::EndPopup()
8414{
8415 ImGuiContext& g = *GImGui;
8416 ImGuiWindow* window = g.CurrentWindow;
8417 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
8418 IM_ASSERT(g.BeginPopupStack.Size > 0);
8419
8420 // Make all menus and popups wrap around for now, may need to expose that policy.
8421 if (g.NavWindow == window)
8422 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
8423
8424 // Child-popups don't need to be laid out
8425 IM_ASSERT(g.WithinEndChild == false);
8426 if (window->Flags & ImGuiWindowFlags_ChildWindow)
8427 g.WithinEndChild = true;
8428 End();
8429 g.WithinEndChild = false;
8430}
8431
8432// Helper to open a popup if mouse button is released over the item
8433// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
8434void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
8435{
8436 ImGuiContext& g = *GImGui;
8437 ImGuiWindow* window = g.CurrentWindow;
8438 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8439 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8440 {
8441 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!
8442 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8443 OpenPopupEx(id, popup_flags);
8444 }
8445}
8446
8447// This is a helper to handle the simplest case of associating one named popup to one given widget.
8448// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
8449// - To create a popup with a specific identifier, pass it in str_id.
8450// - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
8451// - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
8452// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
8453// This is essentially the same as:
8454// id = str_id ? GetID(str_id) : GetItemID();
8455// OpenPopupOnItemClick(str_id);
8456// return BeginPopup(id);
8457// Which is essentially the same as:
8458// id = str_id ? GetID(str_id) : GetItemID();
8459// if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
8460// OpenPopup(id);
8461// return BeginPopup(id);
8462// The main difference being that this is tweaked to avoid computing the ID twice.
8463bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
8464{
8465 ImGuiContext& g = *GImGui;
8466 ImGuiWindow* window = g.CurrentWindow;
8467 if (window->SkipItems)
8468 return false;
8469 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!
8470 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
8471 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8472 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8473 OpenPopupEx(id, popup_flags);
8474 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8475}
8476
8477bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
8478{
8479 ImGuiContext& g = *GImGui;
8480 ImGuiWindow* window = g.CurrentWindow;
8481 if (!str_id)
8482 str_id = "window_context";
8483 ImGuiID id = window->GetID(str_id);
8484 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8485 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
8486 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
8487 OpenPopupEx(id, popup_flags);
8488 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8489}
8490
8491bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
8492{
8493 ImGuiContext& g = *GImGui;
8494 ImGuiWindow* window = g.CurrentWindow;
8495 if (!str_id)
8496 str_id = "void_context";
8497 ImGuiID id = window->GetID(str_id);
8498 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
8499 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
8500 if (GetTopMostPopupModal() == NULL)
8501 OpenPopupEx(id, popup_flags);
8502 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
8503}
8504
8505// 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.)
8506// 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.
8507// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
8508// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
8509// this allows us to have tooltips/popups displayed out of the parent viewport.)
8510ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
8511{
8512 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
8513 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
8514 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
8515
8516 // Combo Box policy (we want a connecting edge)
8517 if (policy == ImGuiPopupPositionPolicy_ComboBox)
8518 {
8519 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
8520 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8521 {
8522 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8523 if (n != -1 && dir == *last_dir) // Already tried this direction?
8524 continue;
8525 ImVec2 pos;
8526 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
8527 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8528 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8529 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8530 if (!r_outer.Contains(ImRect(pos, pos + size)))
8531 continue;
8532 *last_dir = dir;
8533 return pos;
8534 }
8535 }
8536
8537 // Tooltip and Default popup policy
8538 // (Always first try the direction we used on the last frame, if any)
8539 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
8540 {
8541 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8542 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8543 {
8544 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8545 if (n != -1 && dir == *last_dir) // Already tried this direction?
8546 continue;
8547
8548 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);
8549 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);
8550
8551 // 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)
8552 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
8553 continue;
8554 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
8555 continue;
8556
8557 ImVec2 pos;
8558 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8559 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
8560
8561 // Clamp top-left corner of popup
8562 pos.x = ImMax(pos.x, r_outer.Min.x);
8563 pos.y = ImMax(pos.y, r_outer.Min.y);
8564
8565 *last_dir = dir;
8566 return pos;
8567 }
8568 }
8569
8570 // Fallback when not enough room:
8571 *last_dir = ImGuiDir_None;
8572
8573 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8574 if (policy == ImGuiPopupPositionPolicy_Tooltip)
8575 return ref_pos + ImVec2(2, 2);
8576
8577 // Otherwise try to keep within display
8578 ImVec2 pos = ref_pos;
8579 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8580 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8581 return pos;
8582}
8583
8584// Note that this is used for popups, which can overlap the non work-area of individual viewports.
8585ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
8586{
8587 ImGuiContext& g = *GImGui;
8588 IM_UNUSED(window);
8589 ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
8590 ImVec2 padding = g.Style.DisplaySafeAreaPadding;
8591 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8592 return r_screen;
8593}
8594
8595ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8596{
8597 ImGuiContext& g = *GImGui;
8598
8599 ImRect r_outer = GetPopupAllowedExtentRect(window);
8600 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8601 {
8602 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8603 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8604 IM_ASSERT(g.CurrentWindow == window);
8605 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
8606 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).
8607 ImRect r_avoid;
8608 if (parent_window->DC.MenuBarAppending)
8609 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
8610 else
8611 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);
8612 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8613 }
8614 if (window->Flags & ImGuiWindowFlags_Popup)
8615 {
8616 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8617 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
8618 }
8619 if (window->Flags & ImGuiWindowFlags_Tooltip)
8620 {
8621 // Position tooltip (always follows mouse)
8622 float sc = g.Style.MouseCursorScale;
8623 ImVec2 ref_pos = NavCalcPreferredRefPos();
8624 ImRect r_avoid;
8625 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8626 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8627 else
8628 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.
8629 return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
8630 }
8631 IM_ASSERT(0);
8632 return window->Pos;
8633}
8634
8635//-----------------------------------------------------------------------------
8636// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8637//-----------------------------------------------------------------------------
8638
8639// FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked.
8640void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8641{
8642 ImGuiContext& g = *GImGui;
8643 IM_ASSERT(g.NavWindow != NULL);
8644 IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
8645 g.NavId = id;
8646 g.NavLayer = nav_layer;
8647 g.NavFocusScopeId = focus_scope_id;
8648 g.NavWindow->NavLastIds[nav_layer] = id;
8649 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8650 //g.NavDisableHighlight = false;
8651 //g.NavDisableMouseHover = g.NavMousePosDirty = true;
8652}
8653
8654void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8655{
8656 ImGuiContext& g = *GImGui;
8657 IM_ASSERT(id != 0);
8658
8659 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8660 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8661 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8662 if (g.NavWindow != window)
8663 g.NavInitRequest = false;
8664 g.NavWindow = window;
8665 g.NavId = id;
8666 g.NavLayer = nav_layer;
8667 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8668 window->NavLastIds[nav_layer] = id;
8669 if (g.LastItemData.ID == id)
8670 window->NavRectRel[nav_layer] = ImRect(g.LastItemData.Rect.Min - window->Pos, g.LastItemData.Rect.Max - window->Pos);
8671
8672 if (g.ActiveIdSource == ImGuiInputSource_Nav)
8673 g.NavDisableMouseHover = true;
8674 else
8675 g.NavDisableHighlight = true;
8676}
8677
8678ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8679{
8680 if (ImFabs(dx) > ImFabs(dy))
8681 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8682 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8683}
8684
8685static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8686{
8687 if (a1 < b0)
8688 return a1 - b0;
8689 if (b1 < a0)
8690 return a0 - b1;
8691 return 0.0f;
8692}
8693
8694static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8695{
8696 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8697 {
8698 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8699 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8700 }
8701 else
8702 {
8703 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8704 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8705 }
8706}
8707
8708// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
8709static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand)
8710{
8711 ImGuiContext& g = *GImGui;
8712 ImGuiWindow* window = g.CurrentWindow;
8713 if (g.NavLayer != window->DC.NavLayerCurrent)
8714 return false;
8715
8716 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)
8717 g.NavScoringCount++;
8718
8719 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8720 if (window->ParentWindow == g.NavWindow)
8721 {
8722 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8723 if (!window->ClipRect.Overlaps(cand))
8724 return false;
8725 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8726 }
8727
8728 // 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)
8729 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8730 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8731
8732 // Compute distance between boxes
8733 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8734 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8735 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
8736 if (dby != 0.0f && dbx != 0.0f)
8737 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8738 float dist_box = ImFabs(dbx) + ImFabs(dby);
8739
8740 // 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)
8741 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8742 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8743 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8744
8745 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8746 ImGuiDir quadrant;
8747 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8748 if (dbx != 0.0f || dby != 0.0f)
8749 {
8750 // For non-overlapping boxes, use distance between boxes
8751 dax = dbx;
8752 day = dby;
8753 dist_axial = dist_box;
8754 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8755 }
8756 else if (dcx != 0.0f || dcy != 0.0f)
8757 {
8758 // For overlapping boxes with different centers, use distance between centers
8759 dax = dcx;
8760 day = dcy;
8761 dist_axial = dist_center;
8762 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8763 }
8764 else
8765 {
8766 // 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)
8767 quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8768 }
8769
8770#if IMGUI_DEBUG_NAV_SCORING
8771 char buf[128];
8772 if (IsMouseHoveringRect(cand.Min, cand.Max))
8773 {
8774 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]);
8775 ImDrawList* draw_list = GetForegroundDrawList(window);
8776 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8777 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8778 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8779 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8780 }
8781 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8782 {
8783 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
8784 if (quadrant == g.NavMoveDir)
8785 {
8786 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8787 ImDrawList* draw_list = GetForegroundDrawList(window);
8788 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8789 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8790 }
8791 }
8792#endif
8793
8794 // Is it in the quadrant we're interesting in moving to?
8795 bool new_best = false;
8796 if (quadrant == g.NavMoveDir)
8797 {
8798 // Does it beat the current best candidate?
8799 if (dist_box < result->DistBox)
8800 {
8801 result->DistBox = dist_box;
8802 result->DistCenter = dist_center;
8803 return true;
8804 }
8805 if (dist_box == result->DistBox)
8806 {
8807 // Try using distance between center points to break ties
8808 if (dist_center < result->DistCenter)
8809 {
8810 result->DistCenter = dist_center;
8811 new_best = true;
8812 }
8813 else if (dist_center == result->DistCenter)
8814 {
8815 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8816 // (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),
8817 // 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.
8818 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8819 new_best = true;
8820 }
8821 }
8822 }
8823
8824 // 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
8825 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8826 // 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.
8827 // 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.
8828 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8829 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
8830 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8831 if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f))
8832 {
8833 result->DistAxial = dist_axial;
8834 new_best = true;
8835 }
8836
8837 return new_best;
8838}
8839
8840static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel)
8841{
8842 result->Window = window;
8843 result->ID = id;
8844 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8845 result->RectRel = nav_bb_rel;
8846}
8847
8848// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
8849static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8850{
8851 ImGuiContext& g = *GImGui;
8852 //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag.
8853 // return;
8854
8855 const ImGuiItemFlags item_flags = g.LastItemData.InFlags;
8856 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8857
8858 // Process Init Request
8859 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8860 {
8861 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8862 const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0;
8863 if (candidate_for_nav_default_focus || g.NavInitResultId == 0)
8864 {
8865 g.NavInitResultId = id;
8866 g.NavInitResultRectRel = nav_bb_rel;
8867 }
8868 if (candidate_for_nav_default_focus)
8869 {
8870 g.NavInitRequest = false; // Found a match, clear request
8871 NavUpdateAnyRequestFlag();
8872 }
8873 }
8874
8875 // Process Move Request (scoring for navigation)
8876 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
8877 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
8878 {
8879 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8880#if IMGUI_DEBUG_NAV_SCORING
8881 // [DEBUG] Score all items in NavWindow at all times
8882 if (!g.NavMoveRequest)
8883 g.NavMoveDir = g.NavMoveDirLast;
8884 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
8885#else
8886 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
8887#endif
8888 if (new_best)
8889 NavApplyItemToResult(result, window, id, nav_bb_rel);
8890
8891 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8892 const float VISIBLE_RATIO = 0.70f;
8893 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8894 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)
8895 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
8896 NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel);
8897 }
8898
8899 // Update window-relative bounding box of navigated item
8900 if (g.NavId == id)
8901 {
8902 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8903 g.NavLayer = window->DC.NavLayerCurrent;
8904 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8905 g.NavIdIsAlive = true;
8906 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
8907 }
8908}
8909
8910bool ImGui::NavMoveRequestButNoResultYet()
8911{
8912 ImGuiContext& g = *GImGui;
8913 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8914}
8915
8916void ImGui::NavMoveRequestCancel()
8917{
8918 ImGuiContext& g = *GImGui;
8919 g.NavMoveRequest = false;
8920 NavUpdateAnyRequestFlag();
8921}
8922
8923void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
8924{
8925 ImGuiContext& g = *GImGui;
8926 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
8927 NavMoveRequestCancel();
8928 g.NavMoveDir = move_dir;
8929 g.NavMoveClipDir = clip_dir;
8930 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
8931 g.NavMoveRequestFlags = move_flags;
8932 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
8933}
8934
8935void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
8936{
8937 ImGuiContext& g = *GImGui;
8938
8939 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
8940 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
8941 g.NavWrapRequestWindow = window;
8942 g.NavWrapRequestFlags = move_flags;
8943}
8944
8945// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8946// This way we could find the last focused window among our children. It would be much less confusing this way?
8947static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8948{
8949 ImGuiWindow* parent = nav_window;
8950 while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8951 parent = parent->ParentWindow;
8952 if (parent && parent != nav_window)
8953 parent->NavLastChildNavWindow = nav_window;
8954}
8955
8956// Restore the last focused child.
8957// Call when we are expected to land on the Main Layer (0) after FocusWindow()
8958static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8959{
8960 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
8961 return window->NavLastChildNavWindow;
8962 return window;
8963}
8964
8965void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
8966{
8967 ImGuiContext& g = *GImGui;
8968 if (layer == ImGuiNavLayer_Main)
8969 g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);
8970 ImGuiWindow* window = g.NavWindow;
8971 if (window->NavLastIds[layer] != 0)
8972 {
8973 SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
8974 g.NavDisableHighlight = false;
8975 g.NavDisableMouseHover = g.NavMousePosDirty = true;
8976 }
8977 else
8978 {
8979 g.NavLayer = layer;
8980 NavInitWindow(window, true);
8981 }
8982}
8983
8984static inline void ImGui::NavUpdateAnyRequestFlag()
8985{
8986 ImGuiContext& g = *GImGui;
8987 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8988 if (g.NavAnyRequest)
8989 IM_ASSERT(g.NavWindow != NULL);
8990}
8991
8992// This needs to be called before we submit any widget (aka in or before Begin)
8993void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8994{
8995 ImGuiContext& g = *GImGui;
8996 IM_ASSERT(window == g.NavWindow);
8997
8998 if (window->Flags & ImGuiWindowFlags_NoNavInputs)
8999 {
9000 g.NavId = g.NavFocusScopeId = 0;
9001 return;
9002 }
9003
9004 bool init_for_nav = false;
9005 if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
9006 init_for_nav = true;
9007 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
9008 if (init_for_nav)
9009 {
9010 SetNavID(0, g.NavLayer, 0, ImRect());
9011 g.NavInitRequest = true;
9012 g.NavInitRequestFromMove = false;
9013 g.NavInitResultId = 0;
9014 g.NavInitResultRectRel = ImRect();
9015 NavUpdateAnyRequestFlag();
9016 }
9017 else
9018 {
9019 g.NavId = window->NavLastIds[0];
9020 g.NavFocusScopeId = 0;
9021 }
9022}
9023
9024static ImVec2 ImGui::NavCalcPreferredRefPos()
9025{
9026 ImGuiContext& g = *GImGui;
9027 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
9028 {
9029 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
9030 if (IsMousePosValid(&g.IO.MousePos))
9031 return g.IO.MousePos;
9032 return g.LastValidMousePos;
9033 }
9034 else
9035 {
9036 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
9037 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
9038 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()));
9039 ImGuiViewport* viewport = GetMainViewport();
9040 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.
9041 }
9042}
9043
9044float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
9045{
9046 ImGuiContext& g = *GImGui;
9047 if (mode == ImGuiInputReadMode_Down)
9048 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
9049
9050 const float t = g.IO.NavInputsDownDuration[n];
9051 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
9052 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
9053 if (t < 0.0f)
9054 return 0.0f;
9055 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
9056 return (t == 0.0f) ? 1.0f : 0.0f;
9057 if (mode == ImGuiInputReadMode_Repeat)
9058 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
9059 if (mode == ImGuiInputReadMode_RepeatSlow)
9060 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
9061 if (mode == ImGuiInputReadMode_RepeatFast)
9062 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
9063 return 0.0f;
9064}
9065
9066ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
9067{
9068 ImVec2 delta(0.0f, 0.0f);
9069 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
9070 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
9071 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
9072 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
9073 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
9074 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
9075 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
9076 delta *= slow_factor;
9077 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
9078 delta *= fast_factor;
9079 return delta;
9080}
9081
9082static void ImGui::NavUpdate()
9083{
9084 ImGuiContext& g = *GImGui;
9085 ImGuiIO& io = g.IO;
9086
9087 io.WantSetMousePos = false;
9088 g.NavWrapRequestWindow = NULL;
9089 g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
9090#if 0
9091 if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
9092#endif
9093
9094 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
9095 // (do it before we map Keyboard input!)
9096 bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
9097 bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
9098 if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
9099 {
9100 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
9101 || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
9102 g.NavInputSource = ImGuiInputSource_Gamepad;
9103 }
9104
9105 // Update Keyboard->Nav inputs mapping
9106 if (nav_keyboard_active)
9107 {
9108 #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)
9109 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
9110 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
9111 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
9112 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
9113 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
9114 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
9115 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
9116 if (io.KeyCtrl)
9117 io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
9118 if (io.KeyShift)
9119 io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
9120
9121 // AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl)
9122 // But also even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.
9123 if (io.KeyAlt && !io.KeyCtrl)
9124 io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
9125
9126 // We automatically cancel toggling nav layer when any text has been typed while holding Alt. (See #370)
9127 if (io.KeyAlt && !io.KeyCtrl && g.NavWindowingToggleLayer && io.InputQueueCharacters.Size > 0)
9128 g.NavWindowingToggleLayer = false;
9129
9130 #undef NAV_MAP_KEY
9131 }
9132 memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
9133 for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
9134 io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
9135
9136 // Process navigation init request (select first/default focus)
9137 if (g.NavInitResultId != 0)
9138 NavUpdateInitResult();
9139 g.NavInitRequest = false;
9140 g.NavInitRequestFromMove = false;
9141 g.NavInitResultId = 0;
9142 g.NavJustMovedToId = 0;
9143
9144 // Process navigation move request
9145 if (g.NavMoveRequest)
9146 NavUpdateMoveResult();
9147
9148 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
9149 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
9150 {
9151 IM_ASSERT(g.NavMoveRequest);
9152 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9153 g.NavDisableHighlight = false;
9154 g.NavMoveRequestForward = ImGuiNavForward_None;
9155 }
9156
9157 // Apply application mouse position movement, after we had a chance to process move request result.
9158 if (g.NavMousePosDirty && g.NavIdIsAlive)
9159 {
9160 // Set mouse position given our knowledge of the navigated item position from last frame
9161 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
9162 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
9163 {
9164 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
9165 io.WantSetMousePos = true;
9166 }
9167 g.NavMousePosDirty = false;
9168 }
9169 g.NavIdIsAlive = false;
9170 g.NavJustTabbedId = 0;
9171 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
9172
9173 // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0
9174 if (g.NavWindow)
9175 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
9176 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
9177 g.NavWindow->NavLastChildNavWindow = NULL;
9178
9179 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
9180 NavUpdateWindowing();
9181
9182 // Set output flags for user application
9183 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
9184 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
9185
9186 // Process NavCancel input (to close a popup, get back to parent, clear focus)
9187 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
9188 {
9189 IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
9190 if (g.ActiveId != 0)
9191 {
9192 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
9193 ClearActiveID();
9194 }
9195 else if (g.NavLayer != ImGuiNavLayer_Main)
9196 {
9197 // Leave the "menu" layer
9198 NavRestoreLayer(ImGuiNavLayer_Main);
9199 }
9200 else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
9201 {
9202 // Exit child window
9203 ImGuiWindow* child_window = g.NavWindow;
9204 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
9205 IM_ASSERT(child_window->ChildId != 0);
9206 ImRect child_rect = child_window->Rect();
9207 FocusWindow(parent_window);
9208 SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos));
9209 }
9210 else if (g.OpenPopupStack.Size > 0)
9211 {
9212 // Close open popup/menu
9213 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
9214 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
9215 }
9216 else
9217 {
9218 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
9219 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
9220 g.NavWindow->NavLastIds[0] = 0;
9221 g.NavId = g.NavFocusScopeId = 0;
9222 }
9223 }
9224
9225 // Process manual activation request
9226 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
9227 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9228 {
9229 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
9230 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
9231 if (g.ActiveId == 0 && activate_pressed)
9232 g.NavActivateId = g.NavId;
9233 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
9234 g.NavActivateDownId = g.NavId;
9235 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
9236 g.NavActivatePressedId = g.NavId;
9237 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
9238 g.NavInputId = g.NavId;
9239 }
9240 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9241 g.NavDisableHighlight = true;
9242 if (g.NavActivateId != 0)
9243 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
9244 g.NavMoveRequest = false;
9245
9246 // Process programmatic activation request
9247 if (g.NavNextActivateId != 0)
9248 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
9249 g.NavNextActivateId = 0;
9250
9251 // Initiate directional inputs request
9252 if (g.NavMoveRequestForward == ImGuiNavForward_None)
9253 {
9254 g.NavMoveDir = ImGuiDir_None;
9255 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
9256 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
9257 {
9258 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
9259 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
9260 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
9261 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
9262 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
9263 }
9264 g.NavMoveClipDir = g.NavMoveDir;
9265 }
9266 else
9267 {
9268 // 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)
9269 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
9270 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
9271 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
9272 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
9273 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
9274 }
9275
9276 // Update PageUp/PageDown/Home/End scroll
9277 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
9278 float nav_scoring_rect_offset_y = 0.0f;
9279 if (nav_keyboard_active)
9280 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
9281
9282 // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match
9283 if (g.NavMoveDir != ImGuiDir_None)
9284 {
9285 g.NavMoveRequest = true;
9286 g.NavMoveRequestKeyMods = io.KeyMods;
9287 g.NavMoveDirLast = g.NavMoveDir;
9288 }
9289 if (g.NavMoveRequest && g.NavId == 0)
9290 {
9291 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
9292 g.NavInitRequest = g.NavInitRequestFromMove = true;
9293 // Reassigning with same value, we're being explicit here.
9294 g.NavInitResultId = 0; // -V1048
9295 g.NavDisableHighlight = false;
9296 }
9297 NavUpdateAnyRequestFlag();
9298
9299 // Scrolling
9300 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
9301 {
9302 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
9303 ImGuiWindow* window = g.NavWindow;
9304 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.
9305 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
9306 {
9307 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
9308 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
9309 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
9310 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
9311 }
9312
9313 // *Normal* Manual scroll with NavScrollXXX keys
9314 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
9315 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
9316 if (scroll_dir.x != 0.0f && window->ScrollbarX)
9317 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
9318 if (scroll_dir.y != 0.0f)
9319 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
9320 }
9321
9322 // Reset search results
9323 g.NavMoveResultLocal.Clear();
9324 g.NavMoveResultLocalVisibleSet.Clear();
9325 g.NavMoveResultOther.Clear();
9326
9327 // When using gamepad, we project the reference nav bounding box into window visible area.
9328 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
9329 // (can't focus a visible object like we can with the mouse).
9330 if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main)
9331 {
9332 ImGuiWindow* window = g.NavWindow;
9333 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
9334 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
9335 {
9336 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
9337 float pad = window->CalcFontSize() * 0.5f;
9338 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
9339 window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
9340 g.NavId = g.NavFocusScopeId = 0;
9341 }
9342 }
9343
9344 // 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)
9345 ImRect nav_rect_rel = g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted() ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
9346 g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0);
9347 g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
9348 g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
9349 g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
9350 IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
9351 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
9352 g.NavScoringCount = 0;
9353#if IMGUI_DEBUG_NAV_RECTS
9354 if (g.NavWindow)
9355 {
9356 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
9357 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]
9358 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); }
9359 }
9360#endif
9361}
9362
9363static void ImGui::NavUpdateInitResult()
9364{
9365 // 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)
9366 ImGuiContext& g = *GImGui;
9367 if (!g.NavWindow)
9368 return;
9369
9370 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
9371 // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
9372 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
9373 SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
9374 if (g.NavInitRequestFromMove)
9375 {
9376 g.NavDisableHighlight = false;
9377 g.NavDisableMouseHover = g.NavMousePosDirty = true;
9378 }
9379}
9380
9381// Apply result from previous frame navigation directional move request
9382static void ImGui::NavUpdateMoveResult()
9383{
9384 ImGuiContext& g = *GImGui;
9385 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
9386 {
9387 // 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)
9388 if (g.NavId != 0)
9389 {
9390 g.NavDisableHighlight = false;
9391 g.NavDisableMouseHover = true;
9392 }
9393 return;
9394 }
9395
9396 // Select which result to use
9397 ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
9398
9399 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
9400 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
9401 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
9402 result = &g.NavMoveResultLocalVisibleSet;
9403
9404 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
9405 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
9406 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
9407 result = &g.NavMoveResultOther;
9408 IM_ASSERT(g.NavWindow && result->Window);
9409
9410 // Scroll to keep newly navigated item fully into view.
9411 if (g.NavLayer == ImGuiNavLayer_Main)
9412 {
9413 ImVec2 delta_scroll;
9414 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
9415 {
9416 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
9417 delta_scroll.y = result->Window->Scroll.y - scroll_target;
9418 SetScrollY(result->Window, scroll_target);
9419 }
9420 else
9421 {
9422 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
9423 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
9424 }
9425
9426 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
9427 result->RectRel.TranslateX(-delta_scroll.x);
9428 result->RectRel.TranslateY(-delta_scroll.y);
9429 }
9430
9431 ClearActiveID();
9432 g.NavWindow = result->Window;
9433 if (g.NavId != result->ID)
9434 {
9435 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
9436 g.NavJustMovedToId = result->ID;
9437 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
9438 g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
9439 }
9440 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
9441 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
9442 g.NavDisableHighlight = false;
9443 g.NavDisableMouseHover = g.NavMousePosDirty = true;
9444}
9445
9446// Handle PageUp/PageDown/Home/End keys
9447static float ImGui::NavUpdatePageUpPageDown()
9448{
9449 ImGuiContext& g = *GImGui;
9450 ImGuiIO& io = g.IO;
9451
9452 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
9453 return 0.0f;
9454 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
9455 return 0.0f;
9456
9457 ImGuiWindow* window = g.NavWindow;
9458 const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
9459 const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
9460 const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
9461 const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
9462 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
9463 {
9464 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
9465 {
9466 // Fallback manual-scroll when window has no navigable item
9467 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9468 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
9469 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9470 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
9471 else if (home_pressed)
9472 SetScrollY(window, 0.0f);
9473 else if (end_pressed)
9474 SetScrollY(window, window->ScrollMax.y);
9475 }
9476 else
9477 {
9478 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
9479 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
9480 float nav_scoring_rect_offset_y = 0.0f;
9481 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
9482 {
9483 nav_scoring_rect_offset_y = -page_offset_y;
9484 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)
9485 g.NavMoveClipDir = ImGuiDir_Up;
9486 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9487 }
9488 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
9489 {
9490 nav_scoring_rect_offset_y = +page_offset_y;
9491 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)
9492 g.NavMoveClipDir = ImGuiDir_Down;
9493 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
9494 }
9495 else if (home_pressed)
9496 {
9497 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
9498 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
9499 // Preserve current horizontal position if we have any.
9500 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
9501 if (nav_rect_rel.IsInverted())
9502 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9503 g.NavMoveDir = ImGuiDir_Down;
9504 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9505 }
9506 else if (end_pressed)
9507 {
9508 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
9509 if (nav_rect_rel.IsInverted())
9510 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
9511 g.NavMoveDir = ImGuiDir_Up;
9512 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
9513 }
9514 return nav_scoring_rect_offset_y;
9515 }
9516 }
9517 return 0.0f;
9518}
9519
9520static void ImGui::NavEndFrame()
9521{
9522 ImGuiContext& g = *GImGui;
9523
9524 // Show CTRL+TAB list window
9525 if (g.NavWindowingTarget != NULL)
9526 NavUpdateWindowingOverlay();
9527
9528 // Perform wrap-around in menus
9529 ImGuiWindow* window = g.NavWrapRequestWindow;
9530 ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
9531 if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
9532 {
9533 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
9534 ImRect bb_rel = window->NavRectRel[0];
9535
9536 ImGuiDir clip_dir = g.NavMoveDir;
9537 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9538 {
9539 bb_rel.Min.x = bb_rel.Max.x =
9540 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
9541 if (move_flags & ImGuiNavMoveFlags_WrapX)
9542 {
9543 bb_rel.TranslateY(-bb_rel.GetHeight());
9544 clip_dir = ImGuiDir_Up;
9545 }
9546 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9547 }
9548 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
9549 {
9550 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
9551 if (move_flags & ImGuiNavMoveFlags_WrapX)
9552 {
9553 bb_rel.TranslateY(+bb_rel.GetHeight());
9554 clip_dir = ImGuiDir_Down;
9555 }
9556 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9557 }
9558 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9559 {
9560 bb_rel.Min.y = bb_rel.Max.y =
9561 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9562 if (move_flags & ImGuiNavMoveFlags_WrapY)
9563 {
9564 bb_rel.TranslateX(-bb_rel.GetWidth());
9565 clip_dir = ImGuiDir_Left;
9566 }
9567 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9568 }
9569 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9570 {
9571 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9572 if (move_flags & ImGuiNavMoveFlags_WrapY)
9573 {
9574 bb_rel.TranslateX(+bb_rel.GetWidth());
9575 clip_dir = ImGuiDir_Right;
9576 }
9577 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9578 }
9579 }
9580}
9581
9582static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
9583{
9584 ImGuiContext& g = *GImGui;
9585 IM_UNUSED(g);
9586 int order = window->FocusOrder;
9587 IM_ASSERT(g.WindowsFocusOrder[order] == window);
9588 return order;
9589}
9590
9591static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9592{
9593 ImGuiContext& g = *GImGui;
9594 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9595 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9596 return g.WindowsFocusOrder[i];
9597 return NULL;
9598}
9599
9600static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9601{
9602 ImGuiContext& g = *GImGui;
9603 IM_ASSERT(g.NavWindowingTarget);
9604 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9605 return;
9606
9607 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9608 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9609 if (!window_target)
9610 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9611 if (window_target) // Don't reset windowing target if there's a single window in the list
9612 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9613 g.NavWindowingToggleLayer = false;
9614}
9615
9616// Windowing management mode
9617// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9618// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
9619static void ImGui::NavUpdateWindowing()
9620{
9621 ImGuiContext& g = *GImGui;
9622 ImGuiWindow* apply_focus_window = NULL;
9623 bool apply_toggle_layer = false;
9624
9625 ImGuiWindow* modal_window = GetTopMostPopupModal();
9626 bool allow_windowing = (modal_window == NULL);
9627 if (!allow_windowing)
9628 g.NavWindowingTarget = NULL;
9629
9630 // Fade out
9631 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9632 {
9633 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
9634 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9635 g.NavWindowingTargetAnim = NULL;
9636 }
9637
9638 // Start CTRL-TAB or Square+L/R window selection
9639 bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9640 bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9641 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9642 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9643 {
9644 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;
9645 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9646 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
9647 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
9648 }
9649
9650 // Gamepad update
9651 g.NavWindowingTimer += g.IO.DeltaTime;
9652 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)
9653 {
9654 // 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
9655 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9656
9657 // Select window to focus
9658 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9659 if (focus_change_dir != 0)
9660 {
9661 NavUpdateWindowingHighlightWindow(focus_change_dir);
9662 g.NavWindowingHighlightAlpha = 1.0f;
9663 }
9664
9665 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9666 if (!IsNavInputDown(ImGuiNavInput_Menu))
9667 {
9668 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9669 if (g.NavWindowingToggleLayer && g.NavWindow)
9670 apply_toggle_layer = true;
9671 else if (!g.NavWindowingToggleLayer)
9672 apply_focus_window = g.NavWindowingTarget;
9673 g.NavWindowingTarget = NULL;
9674 }
9675 }
9676
9677 // Keyboard: Focus
9678 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)
9679 {
9680 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9681 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9682 if (IsKeyPressedMap(ImGuiKey_Tab, true))
9683 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
9684 if (!g.IO.KeyCtrl)
9685 apply_focus_window = g.NavWindowingTarget;
9686 }
9687
9688 // Keyboard: Press and Release ALT to toggle menu layer
9689 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB
9690 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
9691 g.NavWindowingToggleLayer = true;
9692 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
9693 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
9694 apply_toggle_layer = true;
9695
9696 // Move window
9697 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9698 {
9699 ImVec2 move_delta;
9700 if (g.NavInputSource == ImGuiInputSource_Keyboard && !g.IO.KeyShift)
9701 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9702 if (g.NavInputSource == ImGuiInputSource_Gamepad)
9703 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9704 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9705 {
9706 const float NAV_MOVE_SPEED = 800.0f;
9707 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well
9708 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9709 SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9710 MarkIniSettingsDirty(moving_window);
9711 g.NavDisableMouseHover = true;
9712 }
9713 }
9714
9715 // Apply final focus
9716 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
9717 {
9718 ClearActiveID();
9719 g.NavDisableHighlight = false;
9720 g.NavDisableMouseHover = true;
9721 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9722 ClosePopupsOverWindow(apply_focus_window, false);
9723 FocusWindow(apply_focus_window);
9724 if (apply_focus_window->NavLastIds[0] == 0)
9725 NavInitWindow(apply_focus_window, false);
9726
9727 // If the window has ONLY a menu layer (no main layer), select it directly
9728 // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
9729 // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
9730 // the target window as already been previewed once.
9731 // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
9732 // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
9733 // won't be valid.
9734 if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
9735 g.NavLayer = ImGuiNavLayer_Menu;
9736 }
9737 if (apply_focus_window)
9738 g.NavWindowingTarget = NULL;
9739
9740 // Apply menu/layer toggle
9741 if (apply_toggle_layer && g.NavWindow)
9742 {
9743 ClearActiveID();
9744
9745 // Move to parent menu if necessary
9746 ImGuiWindow* new_nav_window = g.NavWindow;
9747 while (new_nav_window->ParentWindow
9748 && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9749 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9750 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9751 new_nav_window = new_nav_window->ParentWindow;
9752 if (new_nav_window != g.NavWindow)
9753 {
9754 ImGuiWindow* old_nav_window = g.NavWindow;
9755 FocusWindow(new_nav_window);
9756 new_nav_window->NavLastChildNavWindow = old_nav_window;
9757 }
9758 g.NavDisableHighlight = false;
9759 g.NavDisableMouseHover = true;
9760
9761 // Reinitialize navigation when entering menu bar with the Alt key.
9762 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9763 if (new_nav_layer == ImGuiNavLayer_Menu)
9764 g.NavWindow->NavLastIds[new_nav_layer] = 0;
9765 NavRestoreLayer(new_nav_layer);
9766 }
9767}
9768
9769// Window has already passed the IsWindowNavFocusable()
9770static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9771{
9772 if (window->Flags & ImGuiWindowFlags_Popup)
9773 return "(Popup)";
9774 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9775 return "(Main menu bar)";
9776 return "(Untitled)";
9777}
9778
9779// Overlay displayed when using CTRL+TAB. Called by EndFrame().
9780void ImGui::NavUpdateWindowingOverlay()
9781{
9782 ImGuiContext& g = *GImGui;
9783 IM_ASSERT(g.NavWindowingTarget != NULL);
9784
9785 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9786 return;
9787
9788 if (g.NavWindowingListWindow == NULL)
9789 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9790 const ImGuiViewport* viewport = GetMainViewport();
9791 SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9792 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9793 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9794 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9795 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9796 {
9797 ImGuiWindow* window = g.WindowsFocusOrder[n];
9798 IM_ASSERT(window != NULL); // Fix static analyzers
9799 if (!IsWindowNavFocusable(window))
9800 continue;
9801 const char* label = window->Name;
9802 if (label == FindRenderedTextEnd(label))
9803 label = GetFallbackWindowNameForWindowingList(window);
9804 Selectable(label, g.NavWindowingTarget == window);
9805 }
9806 End();
9807 PopStyleVar();
9808}
9809
9810
9811//-----------------------------------------------------------------------------
9812// [SECTION] DRAG AND DROP
9813//-----------------------------------------------------------------------------
9814
9815void ImGui::ClearDragDrop()
9816{
9817 ImGuiContext& g = *GImGui;
9818 g.DragDropActive = false;
9819 g.DragDropPayload.Clear();
9820 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9821 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9822 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9823 g.DragDropAcceptFrameCount = -1;
9824
9825 g.DragDropPayloadBufHeap.clear();
9826 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9827}
9828
9829// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
9830// If the item has an identifier:
9831// - This assume/require the item to be activated (typically via ButtonBehavior).
9832// - 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.
9833// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
9834// If the item has no identifier:
9835// - Currently always assume left mouse button.
9836bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9837{
9838 ImGuiContext& g = *GImGui;
9839 ImGuiWindow* window = g.CurrentWindow;
9840
9841 // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
9842 // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
9843 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9844
9845 bool source_drag_active = false;
9846 ImGuiID source_id = 0;
9847 ImGuiID source_parent_id = 0;
9848 if (!(flags & ImGuiDragDropFlags_SourceExtern))
9849 {
9850 source_id = g.LastItemData.ID;
9851 if (source_id != 0)
9852 {
9853 // Common path: items with ID
9854 if (g.ActiveId != source_id)
9855 return false;
9856 if (g.ActiveIdMouseButton != -1)
9857 mouse_button = g.ActiveIdMouseButton;
9858 if (g.IO.MouseDown[mouse_button] == false)
9859 return false;
9860 g.ActiveIdAllowOverlap = false;
9861 }
9862 else
9863 {
9864 // Uncommon path: items without ID
9865 if (g.IO.MouseDown[mouse_button] == false)
9866 return false;
9867
9868 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
9869 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
9870 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
9871 {
9872 IM_ASSERT(0);
9873 return false;
9874 }
9875
9876 // Early out
9877 if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
9878 return false;
9879
9880 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
9881 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
9882 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
9883 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
9884 // Rely on keeping other window->LastItemXXX fields intact.
9885 source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
9886 bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id);
9887 if (is_hovered && g.IO.MouseClicked[mouse_button])
9888 {
9889 SetActiveID(source_id, window);
9890 FocusWindow(window);
9891 }
9892 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
9893 g.ActiveIdAllowOverlap = is_hovered;
9894 }
9895 if (g.ActiveId != source_id)
9896 return false;
9897 source_parent_id = window->IDStack.back();
9898 source_drag_active = IsMouseDragging(mouse_button);
9899
9900 // Disable navigation and key inputs while dragging + cancel existing request if any
9901 SetActiveIdUsingNavAndKeys();
9902 }
9903 else
9904 {
9905 window = NULL;
9906 source_id = ImHashStr("#SourceExtern");
9907 source_drag_active = true;
9908 }
9909
9910 if (source_drag_active)
9911 {
9912 if (!g.DragDropActive)
9913 {
9914 IM_ASSERT(source_id != 0);
9915 ClearDragDrop();
9916 ImGuiPayload& payload = g.DragDropPayload;
9917 payload.SourceId = source_id;
9918 payload.SourceParentId = source_parent_id;
9919 g.DragDropActive = true;
9920 g.DragDropSourceFlags = flags;
9921 g.DragDropMouseButton = mouse_button;
9922 if (payload.SourceId == g.ActiveId)
9923 g.ActiveIdNoClearOnFocusLoss = true;
9924 }
9925 g.DragDropSourceFrameCount = g.FrameCount;
9926 g.DragDropWithinSource = true;
9927
9928 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9929 {
9930 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
9931 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
9932 BeginTooltip();
9933 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
9934 {
9935 ImGuiWindow* tooltip_window = g.CurrentWindow;
9936 tooltip_window->SkipItems = true;
9937 tooltip_window->HiddenFramesCanSkipItems = 1;
9938 }
9939 }
9940
9941 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
9942 g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
9943
9944 return true;
9945 }
9946 return false;
9947}
9948
9949void ImGui::EndDragDropSource()
9950{
9951 ImGuiContext& g = *GImGui;
9952 IM_ASSERT(g.DragDropActive);
9953 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
9954
9955 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9956 EndTooltip();
9957
9958 // Discard the drag if have not called SetDragDropPayload()
9959 if (g.DragDropPayload.DataFrameCount == -1)
9960 ClearDragDrop();
9961 g.DragDropWithinSource = false;
9962}
9963
9964// Use 'cond' to choose to submit payload on drag start or every frame
9965bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
9966{
9967 ImGuiContext& g = *GImGui;
9968 ImGuiPayload& payload = g.DragDropPayload;
9969 if (cond == 0)
9970 cond = ImGuiCond_Always;
9971
9972 IM_ASSERT(type != NULL);
9973 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
9974 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
9975 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
9976 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
9977
9978 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
9979 {
9980 // Copy payload
9981 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
9982 g.DragDropPayloadBufHeap.resize(0);
9983 if (data_size > sizeof(g.DragDropPayloadBufLocal))
9984 {
9985 // Store in heap
9986 g.DragDropPayloadBufHeap.resize((int)data_size);
9987 payload.Data = g.DragDropPayloadBufHeap.Data;
9988 memcpy(payload.Data, data, data_size);
9989 }
9990 else if (data_size > 0)
9991 {
9992 // Store locally
9993 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9994 payload.Data = g.DragDropPayloadBufLocal;
9995 memcpy(payload.Data, data, data_size);
9996 }
9997 else
9998 {
9999 payload.Data = NULL;
10000 }
10001 payload.DataSize = (int)data_size;
10002 }
10003 payload.DataFrameCount = g.FrameCount;
10004
10005 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
10006}
10007
10008bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
10009{
10010 ImGuiContext& g = *GImGui;
10011 if (!g.DragDropActive)
10012 return false;
10013
10014 ImGuiWindow* window = g.CurrentWindow;
10015 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10016 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10017 return false;
10018 IM_ASSERT(id != 0);
10019 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
10020 return false;
10021 if (window->SkipItems)
10022 return false;
10023
10024 IM_ASSERT(g.DragDropWithinTarget == false);
10025 g.DragDropTargetRect = bb;
10026 g.DragDropTargetId = id;
10027 g.DragDropWithinTarget = true;
10028 return true;
10029}
10030
10031// We don't use BeginDragDropTargetCustom() and duplicate its code because:
10032// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
10033// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
10034// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
10035bool ImGui::BeginDragDropTarget()
10036{
10037 ImGuiContext& g = *GImGui;
10038 if (!g.DragDropActive)
10039 return false;
10040
10041 ImGuiWindow* window = g.CurrentWindow;
10042 if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
10043 return false;
10044 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
10045 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
10046 return false;
10047
10048 const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
10049 ImGuiID id = g.LastItemData.ID;
10050 if (id == 0)
10051 id = window->GetIDFromRectangle(display_rect);
10052 if (g.DragDropPayload.SourceId == id)
10053 return false;
10054
10055 IM_ASSERT(g.DragDropWithinTarget == false);
10056 g.DragDropTargetRect = display_rect;
10057 g.DragDropTargetId = id;
10058 g.DragDropWithinTarget = true;
10059 return true;
10060}
10061
10062bool ImGui::IsDragDropPayloadBeingAccepted()
10063{
10064 ImGuiContext& g = *GImGui;
10065 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
10066}
10067
10068const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
10069{
10070 ImGuiContext& g = *GImGui;
10071 ImGuiWindow* window = g.CurrentWindow;
10072 ImGuiPayload& payload = g.DragDropPayload;
10073 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
10074 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
10075 if (type != NULL && !payload.IsDataType(type))
10076 return NULL;
10077
10078 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
10079 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
10080 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
10081 ImRect r = g.DragDropTargetRect;
10082 float r_surface = r.GetWidth() * r.GetHeight();
10083 if (r_surface <= g.DragDropAcceptIdCurrRectSurface)
10084 {
10085 g.DragDropAcceptFlags = flags;
10086 g.DragDropAcceptIdCurr = g.DragDropTargetId;
10087 g.DragDropAcceptIdCurrRectSurface = r_surface;
10088 }
10089
10090 // Render default drop visuals
10091 // FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
10092 payload.Preview = was_accepted_previously;
10093 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
10094 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
10095 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);
10096
10097 g.DragDropAcceptFrameCount = g.FrameCount;
10098 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()
10099 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
10100 return NULL;
10101
10102 return &payload;
10103}
10104
10105const ImGuiPayload* ImGui::GetDragDropPayload()
10106{
10107 ImGuiContext& g = *GImGui;
10108 return g.DragDropActive ? &g.DragDropPayload : NULL;
10109}
10110
10111// We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
10112void ImGui::EndDragDropTarget()
10113{
10114 ImGuiContext& g = *GImGui;
10115 IM_ASSERT(g.DragDropActive);
10116 IM_ASSERT(g.DragDropWithinTarget);
10117 g.DragDropWithinTarget = false;
10118}
10119
10120//-----------------------------------------------------------------------------
10121// [SECTION] LOGGING/CAPTURING
10122//-----------------------------------------------------------------------------
10123// All text output from the interface can be captured into tty/file/clipboard.
10124// By default, tree nodes are automatically opened during logging.
10125//-----------------------------------------------------------------------------
10126
10127// Pass text data straight to log (without being displayed)
10128static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
10129{
10130 if (g.LogFile)
10131 {
10132 g.LogBuffer.Buf.resize(0);
10133 g.LogBuffer.appendfv(fmt, args);
10134 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
10135 }
10136 else
10137 {
10138 g.LogBuffer.appendfv(fmt, args);
10139 }
10140}
10141
10142void ImGui::LogText(const char* fmt, ...)
10143{
10144 ImGuiContext& g = *GImGui;
10145 if (!g.LogEnabled)
10146 return;
10147
10148 va_list args;
10149 va_start(args, fmt);
10150 LogTextV(g, fmt, args);
10151 va_end(args);
10152}
10153
10154void ImGui::LogTextV(const char* fmt, va_list args)
10155{
10156 ImGuiContext& g = *GImGui;
10157 if (!g.LogEnabled)
10158 return;
10159
10160 LogTextV(g, fmt, args);
10161}
10162
10163// Internal version that takes a position to decide on newline placement and pad items according to their depth.
10164// We split text into individual lines to add current tree level padding
10165// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
10166void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
10167{
10168 ImGuiContext& g = *GImGui;
10169 ImGuiWindow* window = g.CurrentWindow;
10170
10171 const char* prefix = g.LogNextPrefix;
10172 const char* suffix = g.LogNextSuffix;
10173 g.LogNextPrefix = g.LogNextSuffix = NULL;
10174
10175 if (!text_end)
10176 text_end = FindRenderedTextEnd(text, text_end);
10177
10178 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
10179 if (ref_pos)
10180 g.LogLinePosY = ref_pos->y;
10181 if (log_new_line)
10182 {
10183 LogText(IM_NEWLINE);
10184 g.LogLineFirstItem = true;
10185 }
10186
10187 if (prefix)
10188 LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here.
10189
10190 // Re-adjust padding if we have popped out of our starting depth
10191 if (g.LogDepthRef > window->DC.TreeDepth)
10192 g.LogDepthRef = window->DC.TreeDepth;
10193 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
10194
10195 const char* text_remaining = text;
10196 for (;;)
10197 {
10198 // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
10199 // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
10200 const char* line_start = text_remaining;
10201 const char* line_end = ImStreolRange(line_start, text_end);
10202 const bool is_last_line = (line_end == text_end);
10203 if (line_start != line_end || !is_last_line)
10204 {
10205 const int line_length = (int)(line_end - line_start);
10206 const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
10207 LogText("%*s%.*s", indentation, "", line_length, line_start);
10208 g.LogLineFirstItem = false;
10209 if (*line_end == '\n')
10210 {
10211 LogText(IM_NEWLINE);
10212 g.LogLineFirstItem = true;
10213 }
10214 }
10215 if (is_last_line)
10216 break;
10217 text_remaining = line_end + 1;
10218 }
10219
10220 if (suffix)
10221 LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));
10222}
10223
10224// Start logging/capturing text output
10225void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
10226{
10227 ImGuiContext& g = *GImGui;
10228 ImGuiWindow* window = g.CurrentWindow;
10229 IM_ASSERT(g.LogEnabled == false);
10230 IM_ASSERT(g.LogFile == NULL);
10231 IM_ASSERT(g.LogBuffer.empty());
10232 g.LogEnabled = true;
10233 g.LogType = type;
10234 g.LogNextPrefix = g.LogNextSuffix = NULL;
10235 g.LogDepthRef = window->DC.TreeDepth;
10236 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
10237 g.LogLinePosY = FLT_MAX;
10238 g.LogLineFirstItem = true;
10239}
10240
10241// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
10242void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
10243{
10244 ImGuiContext& g = *GImGui;
10245 g.LogNextPrefix = prefix;
10246 g.LogNextSuffix = suffix;
10247}
10248
10249void ImGui::LogToTTY(int auto_open_depth)
10250{
10251 ImGuiContext& g = *GImGui;
10252 if (g.LogEnabled)
10253 return;
10254 IM_UNUSED(auto_open_depth);
10255#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10256 LogBegin(ImGuiLogType_TTY, auto_open_depth);
10257 g.LogFile = stdout;
10258#endif
10259}
10260
10261// Start logging/capturing text output to given file
10262void ImGui::LogToFile(int auto_open_depth, const char* filename)
10263{
10264 ImGuiContext& g = *GImGui;
10265 if (g.LogEnabled)
10266 return;
10267
10268 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
10269 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
10270 // By opening the file in binary mode "ab" we have consistent output everywhere.
10271 if (!filename)
10272 filename = g.IO.LogFilename;
10273 if (!filename || !filename[0])
10274 return;
10275 ImFileHandle f = ImFileOpen(filename, "ab");
10276 if (!f)
10277 {
10278 IM_ASSERT(0);
10279 return;
10280 }
10281
10282 LogBegin(ImGuiLogType_File, auto_open_depth);
10283 g.LogFile = f;
10284}
10285
10286// Start logging/capturing text output to clipboard
10287void ImGui::LogToClipboard(int auto_open_depth)
10288{
10289 ImGuiContext& g = *GImGui;
10290 if (g.LogEnabled)
10291 return;
10292 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
10293}
10294
10295void ImGui::LogToBuffer(int auto_open_depth)
10296{
10297 ImGuiContext& g = *GImGui;
10298 if (g.LogEnabled)
10299 return;
10300 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
10301}
10302
10303void ImGui::LogFinish()
10304{
10305 ImGuiContext& g = *GImGui;
10306 if (!g.LogEnabled)
10307 return;
10308
10309 LogText(IM_NEWLINE);
10310 switch (g.LogType)
10311 {
10312 case ImGuiLogType_TTY:
10313#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10314 fflush(g.LogFile);
10315#endif
10316 break;
10317 case ImGuiLogType_File:
10318 ImFileClose(g.LogFile);
10319 break;
10320 case ImGuiLogType_Buffer:
10321 break;
10322 case ImGuiLogType_Clipboard:
10323 if (!g.LogBuffer.empty())
10324 SetClipboardText(g.LogBuffer.begin());
10325 break;
10326 case ImGuiLogType_None:
10327 IM_ASSERT(0);
10328 break;
10329 }
10330
10331 g.LogEnabled = false;
10332 g.LogType = ImGuiLogType_None;
10333 g.LogFile = NULL;
10334 g.LogBuffer.clear();
10335}
10336
10337// Helper to display logging buttons
10338// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
10339void ImGui::LogButtons()
10340{
10341 ImGuiContext& g = *GImGui;
10342
10343 PushID("LogButtons");
10344#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
10345 const bool log_to_tty = Button("Log To TTY"); SameLine();
10346#else
10347 const bool log_to_tty = false;
10348#endif
10349 const bool log_to_file = Button("Log To File"); SameLine();
10350 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
10351 PushAllowKeyboardFocus(false);
10352 SetNextItemWidth(80.0f);
10353 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
10354 PopAllowKeyboardFocus();
10355 PopID();
10356
10357 // Start logging at the end of the function so that the buttons don't appear in the log
10358 if (log_to_tty)
10359 LogToTTY();
10360 if (log_to_file)
10361 LogToFile();
10362 if (log_to_clipboard)
10363 LogToClipboard();
10364}
10365
10366
10367//-----------------------------------------------------------------------------
10368// [SECTION] SETTINGS
10369//-----------------------------------------------------------------------------
10370// - UpdateSettings() [Internal]
10371// - MarkIniSettingsDirty() [Internal]
10372// - CreateNewWindowSettings() [Internal]
10373// - FindWindowSettings() [Internal]
10374// - FindOrCreateWindowSettings() [Internal]
10375// - FindSettingsHandler() [Internal]
10376// - ClearIniSettings() [Internal]
10377// - LoadIniSettingsFromDisk()
10378// - LoadIniSettingsFromMemory()
10379// - SaveIniSettingsToDisk()
10380// - SaveIniSettingsToMemory()
10381// - WindowSettingsHandler_***() [Internal]
10382//-----------------------------------------------------------------------------
10383
10384// Called by NewFrame()
10385void ImGui::UpdateSettings()
10386{
10387 // Load settings on first frame (if not explicitly loaded manually before)
10388 ImGuiContext& g = *GImGui;
10389 if (!g.SettingsLoaded)
10390 {
10391 IM_ASSERT(g.SettingsWindows.empty());
10392 if (g.IO.IniFilename)
10393 LoadIniSettingsFromDisk(g.IO.IniFilename);
10394 g.SettingsLoaded = true;
10395 }
10396
10397 // Save settings (with a delay after the last modification, so we don't spam disk too much)
10398 if (g.SettingsDirtyTimer > 0.0f)
10399 {
10400 g.SettingsDirtyTimer -= g.IO.DeltaTime;
10401 if (g.SettingsDirtyTimer <= 0.0f)
10402 {
10403 if (g.IO.IniFilename != NULL)
10404 SaveIniSettingsToDisk(g.IO.IniFilename);
10405 else
10406 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
10407 g.SettingsDirtyTimer = 0.0f;
10408 }
10409 }
10410}
10411
10412void ImGui::MarkIniSettingsDirty()
10413{
10414 ImGuiContext& g = *GImGui;
10415 if (g.SettingsDirtyTimer <= 0.0f)
10416 g.SettingsDirtyTimer = g.IO.IniSavingRate;
10417}
10418
10419void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
10420{
10421 ImGuiContext& g = *GImGui;
10422 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
10423 if (g.SettingsDirtyTimer <= 0.0f)
10424 g.SettingsDirtyTimer = g.IO.IniSavingRate;
10425}
10426
10427ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
10428{
10429 ImGuiContext& g = *GImGui;
10430
10431#if !IMGUI_DEBUG_INI_SETTINGS
10432 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
10433 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
10434 if (const char* p = strstr(name, "###"))
10435 name = p;
10436#endif
10437 const size_t name_len = strlen(name);
10438
10439 // Allocate chunk
10440 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
10441 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
10442 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
10443 settings->ID = ImHashStr(name, name_len);
10444 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
10445
10446 return settings;
10447}
10448
10449ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
10450{
10451 ImGuiContext& g = *GImGui;
10452 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10453 if (settings->ID == id)
10454 return settings;
10455 return NULL;
10456}
10457
10458ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
10459{
10460 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
10461 return settings;
10462 return CreateNewWindowSettings(name);
10463}
10464
10465ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
10466{
10467 ImGuiContext& g = *GImGui;
10468 const ImGuiID type_hash = ImHashStr(type_name);
10469 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10470 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
10471 return &g.SettingsHandlers[handler_n];
10472 return NULL;
10473}
10474
10475void ImGui::ClearIniSettings()
10476{
10477 ImGuiContext& g = *GImGui;
10478 g.SettingsIniData.clear();
10479 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10480 if (g.SettingsHandlers[handler_n].ClearAllFn)
10481 g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
10482}
10483
10484void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
10485{
10486 size_t file_data_size = 0;
10487 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
10488 if (!file_data)
10489 return;
10490 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
10491 IM_FREE(file_data);
10492}
10493
10494// Zero-tolerance, no error reporting, cheap .ini parsing
10495void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
10496{
10497 ImGuiContext& g = *GImGui;
10498 IM_ASSERT(g.Initialized);
10499 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
10500 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
10501
10502 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
10503 // 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..
10504 if (ini_size == 0)
10505 ini_size = strlen(ini_data);
10506 g.SettingsIniData.Buf.resize((int)ini_size + 1);
10507 char* const buf = g.SettingsIniData.Buf.Data;
10508 char* const buf_end = buf + ini_size;
10509 memcpy(buf, ini_data, ini_size);
10510 buf_end[0] = 0;
10511
10512 // Call pre-read handlers
10513 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
10514 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10515 if (g.SettingsHandlers[handler_n].ReadInitFn)
10516 g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
10517
10518 void* entry_data = NULL;
10519 ImGuiSettingsHandler* entry_handler = NULL;
10520
10521 char* line_end = NULL;
10522 for (char* line = buf; line < buf_end; line = line_end + 1)
10523 {
10524 // Skip new lines markers, then find end of the line
10525 while (*line == '\n' || *line == '\r')
10526 line++;
10527 line_end = line;
10528 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
10529 line_end++;
10530 line_end[0] = 0;
10531 if (line[0] == ';')
10532 continue;
10533 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
10534 {
10535 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
10536 line_end[-1] = 0;
10537 const char* name_end = line_end - 1;
10538 const char* type_start = line + 1;
10539 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
10540 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
10541 if (!type_end || !name_start)
10542 continue;
10543 *type_end = 0; // Overwrite first ']'
10544 name_start++; // Skip second '['
10545 entry_handler = FindSettingsHandler(type_start);
10546 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
10547 }
10548 else if (entry_handler != NULL && entry_data != NULL)
10549 {
10550 // Let type handler parse the line
10551 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
10552 }
10553 }
10554 g.SettingsLoaded = true;
10555
10556 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
10557 memcpy(buf, ini_data, ini_size);
10558
10559 // Call post-read handlers
10560 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10561 if (g.SettingsHandlers[handler_n].ApplyAllFn)
10562 g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
10563}
10564
10565void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
10566{
10567 ImGuiContext& g = *GImGui;
10568 g.SettingsDirtyTimer = 0.0f;
10569 if (!ini_filename)
10570 return;
10571
10572 size_t ini_data_size = 0;
10573 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
10574 ImFileHandle f = ImFileOpen(ini_filename, "wt");
10575 if (!f)
10576 return;
10577 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
10578 ImFileClose(f);
10579}
10580
10581// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
10582const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
10583{
10584 ImGuiContext& g = *GImGui;
10585 g.SettingsDirtyTimer = 0.0f;
10586 g.SettingsIniData.Buf.resize(0);
10587 g.SettingsIniData.Buf.push_back(0);
10588 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
10589 {
10590 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
10591 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
10592 }
10593 if (out_size)
10594 *out_size = (size_t)g.SettingsIniData.size();
10595 return g.SettingsIniData.c_str();
10596}
10597
10598static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10599{
10600 ImGuiContext& g = *ctx;
10601 for (int i = 0; i != g.Windows.Size; i++)
10602 g.Windows[i]->SettingsOffset = -1;
10603 g.SettingsWindows.clear();
10604}
10605
10606static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
10607{
10608 ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10609 ImGuiID id = settings->ID;
10610 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10611 settings->ID = id;
10612 settings->WantApply = true;
10613 return (void*)settings;
10614}
10615
10616static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10617{
10618 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10619 int x, y;
10620 int i;
10621 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
10622 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
10623 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
10624}
10625
10626// Apply to existing windows (if any)
10627static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10628{
10629 ImGuiContext& g = *ctx;
10630 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10631 if (settings->WantApply)
10632 {
10633 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10634 ApplyWindowSettings(window, settings);
10635 settings->WantApply = false;
10636 }
10637}
10638
10639static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10640{
10641 // Gather data from windows that were active during this session
10642 // (if a window wasn't opened in this session we preserve its settings)
10643 ImGuiContext& g = *ctx;
10644 for (int i = 0; i != g.Windows.Size; i++)
10645 {
10646 ImGuiWindow* window = g.Windows[i];
10647 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10648 continue;
10649
10650 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10651 if (!settings)
10652 {
10653 settings = ImGui::CreateNewWindowSettings(window->Name);
10654 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10655 }
10656 IM_ASSERT(settings->ID == window->ID);
10657 settings->Pos = ImVec2ih(window->Pos);
10658 settings->Size = ImVec2ih(window->SizeFull);
10659
10660 settings->Collapsed = window->Collapsed;
10661 }
10662
10663 // Write to text buffer
10664 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10665 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10666 {
10667 const char* settings_name = settings->GetName();
10668 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10669 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10670 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10671 buf->appendf("Collapsed=%d\n", settings->Collapsed);
10672 buf->append("\n");
10673 }
10674}
10675
10676
10677//-----------------------------------------------------------------------------
10678// [SECTION] VIEWPORTS, PLATFORM WINDOWS
10679//-----------------------------------------------------------------------------
10680// - GetMainViewport()
10681// - UpdateViewportsNewFrame() [Internal]
10682// (this section is more complete in the 'docking' branch)
10683//-----------------------------------------------------------------------------
10684
10685ImGuiViewport* ImGui::GetMainViewport()
10686{
10687 ImGuiContext& g = *GImGui;
10688 return g.Viewports[0];
10689}
10690
10691// Update viewports and monitor infos
10692static void ImGui::UpdateViewportsNewFrame()
10693{
10694 ImGuiContext& g = *GImGui;
10695 IM_ASSERT(g.Viewports.Size == 1);
10696
10697 // Update main viewport with current platform position.
10698 // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
10699 ImGuiViewportP* main_viewport = g.Viewports[0];
10700 main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
10701 main_viewport->Pos = ImVec2(0.0f, 0.0f);
10702 main_viewport->Size = g.IO.DisplaySize;
10703
10704 for (int n = 0; n < g.Viewports.Size; n++)
10705 {
10706 ImGuiViewportP* viewport = g.Viewports[n];
10707
10708 // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.
10709 viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;
10710 viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;
10711 viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);
10712 viewport->UpdateWorkRect();
10713 }
10714}
10715
10716//-----------------------------------------------------------------------------
10717// [SECTION] DOCKING
10718//-----------------------------------------------------------------------------
10719
10720// (this section is filled in the 'docking' branch)
10721
10722
10723//-----------------------------------------------------------------------------
10724// [SECTION] PLATFORM DEPENDENT HELPERS
10725//-----------------------------------------------------------------------------
10726
10727#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10728
10729#ifdef _MSC_VER
10730#pragma comment(lib, "user32")
10731#pragma comment(lib, "kernel32")
10732#endif
10733
10734// Win32 clipboard implementation
10735// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
10736static const char* GetClipboardTextFn_DefaultImpl(void*)
10737{
10738 ImGuiContext& g = *GImGui;
10739 g.ClipboardHandlerData.clear();
10740 if (!::OpenClipboard(NULL))
10741 return NULL;
10742 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
10743 if (wbuf_handle == NULL)
10744 {
10745 ::CloseClipboard();
10746 return NULL;
10747 }
10748 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
10749 {
10750 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
10751 g.ClipboardHandlerData.resize(buf_len);
10752 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
10753 }
10754 ::GlobalUnlock(wbuf_handle);
10755 ::CloseClipboard();
10756 return g.ClipboardHandlerData.Data;
10757}
10758
10759static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10760{
10761 if (!::OpenClipboard(NULL))
10762 return;
10763 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
10764 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
10765 if (wbuf_handle == NULL)
10766 {
10767 ::CloseClipboard();
10768 return;
10769 }
10770 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
10771 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
10772 ::GlobalUnlock(wbuf_handle);
10773 ::EmptyClipboard();
10774 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
10775 ::GlobalFree(wbuf_handle);
10776 ::CloseClipboard();
10777}
10778
10779#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
10780
10781#include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
10782static PasteboardRef main_clipboard = 0;
10783
10784// OSX clipboard implementation
10785// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
10786static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10787{
10788 if (!main_clipboard)
10789 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10790 PasteboardClear(main_clipboard);
10791 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
10792 if (cf_data)
10793 {
10794 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
10795 CFRelease(cf_data);
10796 }
10797}
10798
10799static const char* GetClipboardTextFn_DefaultImpl(void*)
10800{
10801 if (!main_clipboard)
10802 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10803 PasteboardSynchronize(main_clipboard);
10804
10805 ItemCount item_count = 0;
10806 PasteboardGetItemCount(main_clipboard, &item_count);
10807 for (ItemCount i = 0; i < item_count; i++)
10808 {
10809 PasteboardItemID item_id = 0;
10810 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
10811 CFArrayRef flavor_type_array = 0;
10812 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
10813 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
10814 {
10815 CFDataRef cf_data;
10816 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
10817 {
10818 ImGuiContext& g = *GImGui;
10819 g.ClipboardHandlerData.clear();
10820 int length = (int)CFDataGetLength(cf_data);
10821 g.ClipboardHandlerData.resize(length + 1);
10822 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
10823 g.ClipboardHandlerData[length] = 0;
10824 CFRelease(cf_data);
10825 return g.ClipboardHandlerData.Data;
10826 }
10827 }
10828 }
10829 return NULL;
10830}
10831
10832#else
10833
10834// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
10835static const char* GetClipboardTextFn_DefaultImpl(void*)
10836{
10837 ImGuiContext& g = *GImGui;
10838 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
10839}
10840
10841static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10842{
10843 ImGuiContext& g = *GImGui;
10844 g.ClipboardHandlerData.clear();
10845 const char* text_end = text + strlen(text);
10846 g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
10847 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
10848 g.ClipboardHandlerData[(int)(text_end - text)] = 0;
10849}
10850
10851#endif
10852
10853// Win32 API IME support (for Asian languages, etc.)
10854#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10855
10856#include <imm.h>
10857#ifdef _MSC_VER
10858#pragma comment(lib, "imm32")
10859#endif
10860
10861static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10862{
10863 // Notify OS Input Method Editor of text input position
10864 ImGuiIO& io = ImGui::GetIO();
10865 if (HWND hwnd = (HWND)io.ImeWindowHandle)
10866 if (HIMC himc = ::ImmGetContext(hwnd))
10867 {
10868 COMPOSITIONFORM cf;
10869 cf.ptCurrentPos.x = x;
10870 cf.ptCurrentPos.y = y;
10871 cf.dwStyle = CFS_FORCE_POSITION;
10872 ::ImmSetCompositionWindow(himc, &cf);
10873 ::ImmReleaseContext(hwnd, himc);
10874 }
10875}
10876
10877#else
10878
10879static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10880
10881#endif
10882
10883//-----------------------------------------------------------------------------
10884// [SECTION] METRICS/DEBUGGER WINDOW
10885//-----------------------------------------------------------------------------
10886// - RenderViewportThumbnail() [Internal]
10887// - RenderViewportsThumbnails() [Internal]
10888// - MetricsHelpMarker() [Internal]
10889// - ShowMetricsWindow()
10890// - DebugNodeColumns() [Internal]
10891// - DebugNodeDrawList() [Internal]
10892// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
10893// - DebugNodeStorage() [Internal]
10894// - DebugNodeTabBar() [Internal]
10895// - DebugNodeViewport() [Internal]
10896// - DebugNodeWindow() [Internal]
10897// - DebugNodeWindowSettings() [Internal]
10898// - DebugNodeWindowsList() [Internal]
10899//-----------------------------------------------------------------------------
10900
10901#ifndef IMGUI_DISABLE_METRICS_WINDOW
10902
10903void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
10904{
10905 ImGuiContext& g = *GImGui;
10906 ImGuiWindow* window = g.CurrentWindow;
10907
10908 ImVec2 scale = bb.GetSize() / viewport->Size;
10909 ImVec2 off = bb.Min - viewport->Pos * scale;
10910 float alpha_mul = 1.0f;
10911 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
10912 for (int i = 0; i != g.Windows.Size; i++)
10913 {
10914 ImGuiWindow* thumb_window = g.Windows[i];
10915 if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
10916 continue;
10917
10918 ImRect thumb_r = thumb_window->Rect();
10919 ImRect title_r = thumb_window->TitleBarRect();
10920 thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale));
10921 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
10922 thumb_r.ClipWithFull(bb);
10923 title_r.ClipWithFull(bb);
10924 const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
10925 window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
10926 window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
10927 window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
10928 window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
10929 }
10930 draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
10931}
10932
10933static void RenderViewportsThumbnails()
10934{
10935 ImGuiContext& g = *GImGui;
10936 ImGuiWindow* window = g.CurrentWindow;
10937
10938 // 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.
10939 float SCALE = 1.0f / 8.0f;
10940 ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
10941 for (int n = 0; n < g.Viewports.Size; n++)
10942 bb_full.Add(g.Viewports[n]->GetMainRect());
10943 ImVec2 p = window->DC.CursorPos;
10944 ImVec2 off = p - bb_full.Min * SCALE;
10945 for (int n = 0; n < g.Viewports.Size; n++)
10946 {
10947 ImGuiViewportP* viewport = g.Viewports[n];
10948 ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
10949 ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
10950 }
10951 ImGui::Dummy(bb_full.GetSize() * SCALE);
10952}
10953
10954// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
10955static void MetricsHelpMarker(const char* desc)
10956{
10957 ImGui::TextDisabled("(?)");
10958 if (ImGui::IsItemHovered())
10959 {
10960 ImGui::BeginTooltip();
10961 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
10962 ImGui::TextUnformatted(desc);
10963 ImGui::PopTextWrapPos();
10964 ImGui::EndTooltip();
10965 }
10966}
10967
10968#ifndef IMGUI_DISABLE_DEMO_WINDOWS
10969namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); }
10970#endif
10971
10972void ImGui::ShowMetricsWindow(bool* p_open)
10973{
10974 if (!Begin("Dear ImGui Metrics/Debugger", p_open))
10975 {
10976 End();
10977 return;
10978 }
10979
10980 ImGuiContext& g = *GImGui;
10981 ImGuiIO& io = g.IO;
10982 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
10983
10984 // Basic info
10985 Text("Dear ImGui %s", GetVersion());
10986 Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
10987 Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
10988 Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
10989 Text("%d active allocations", io.MetricsActiveAllocations);
10990 //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
10991
10992 Separator();
10993
10994 // Debugging enums
10995 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
10996 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
10997 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
10998 const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
10999 if (cfg->ShowWindowsRectsType < 0)
11000 cfg->ShowWindowsRectsType = WRT_WorkRect;
11001 if (cfg->ShowTablesRectsType < 0)
11002 cfg->ShowTablesRectsType = TRT_WorkRect;
11003
11004 struct Funcs
11005 {
11006 static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
11007 {
11008 if (rect_type == TRT_OuterRect) { return table->OuterRect; }
11009 else if (rect_type == TRT_InnerRect) { return table->InnerRect; }
11010 else if (rect_type == TRT_WorkRect) { return table->WorkRect; }
11011 else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; }
11012 else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; }
11013 else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; }
11014 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); }
11015 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); }
11016 else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
11017 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
11018 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); }
11019 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); }
11020 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); }
11021 IM_ASSERT(0);
11022 return ImRect();
11023 }
11024
11025 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
11026 {
11027 if (rect_type == WRT_OuterRect) { return window->Rect(); }
11028 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
11029 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
11030 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
11031 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
11032 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
11033 else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
11034 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
11035 IM_ASSERT(0);
11036 return ImRect();
11037 }
11038 };
11039
11040 // Tools
11041 if (TreeNode("Tools"))
11042 {
11043 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
11044 if (Button("Item Picker.."))
11045 DebugStartItemPicker();
11046 SameLine();
11047 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.");
11048
11049 Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
11050 Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
11051 SameLine();
11052 SetNextItemWidth(GetFontSize() * 12);
11053 cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
11054 if (cfg->ShowWindowsRects && g.NavWindow != NULL)
11055 {
11056 BulletText("'%s':", g.NavWindow->Name);
11057 Indent();
11058 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
11059 {
11060 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
11061 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]);
11062 }
11063 Unindent();
11064 }
11065 Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
11066 Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
11067
11068 Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
11069 SameLine();
11070 SetNextItemWidth(GetFontSize() * 12);
11071 cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
11072 if (cfg->ShowTablesRects && g.NavWindow != NULL)
11073 {
11074 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
11075 {
11076 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
11077 if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
11078 continue;
11079
11080 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
11081 if (IsItemHovered())
11082 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);
11083 Indent();
11084 char buf[128];
11085 for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
11086 {
11087 if (rect_n >= TRT_ColumnsRect)
11088 {
11089 if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
11090 continue;
11091 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
11092 {
11093 ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
11094 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]);
11095 Selectable(buf);
11096 if (IsItemHovered())
11097 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11098 }
11099 }
11100 else
11101 {
11102 ImRect r = Funcs::GetTableRect(table, rect_n, -1);
11103 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]);
11104 Selectable(buf);
11105 if (IsItemHovered())
11106 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
11107 }
11108 }
11109 Unindent();
11110 }
11111 }
11112
11113 TreePop();
11114 }
11115
11116 // Windows
11117 DebugNodeWindowsList(&g.Windows, "Windows");
11118 //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder");
11119
11120 // DrawLists
11121 int drawlist_count = 0;
11122 for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
11123 drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount();
11124 if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
11125 {
11126 for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++)
11127 {
11128 ImGuiViewportP* viewport = g.Viewports[viewport_i];
11129 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
11130 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
11131 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
11132 }
11133 TreePop();
11134 }
11135
11136 // Viewports
11137 if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
11138 {
11139 Indent(GetTreeNodeToLabelSpacing());
11140 RenderViewportsThumbnails();
11141 Unindent(GetTreeNodeToLabelSpacing());
11142 for (int i = 0; i < g.Viewports.Size; i++)
11143 DebugNodeViewport(g.Viewports[i]);
11144 TreePop();
11145 }
11146
11147 // Details for Popups
11148 if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
11149 {
11150 for (int i = 0; i < g.OpenPopupStack.Size; i++)
11151 {
11152 ImGuiWindow* window = g.OpenPopupStack[i].Window;
11153 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" : "");
11154 }
11155 TreePop();
11156 }
11157
11158 // Details for TabBars
11159 if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
11160 {
11161 for (int n = 0; n < g.TabBars.GetMapSize(); n++)
11162 if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
11163 {
11164 PushID(tab_bar);
11165 DebugNodeTabBar(tab_bar, "TabBar");
11166 PopID();
11167 }
11168 TreePop();
11169 }
11170
11171 // Details for Tables
11172 if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
11173 {
11174 for (int n = 0; n < g.Tables.GetMapSize(); n++)
11175 if (ImGuiTable* table = g.Tables.TryGetMapData(n))
11176 DebugNodeTable(table);
11177 TreePop();
11178 }
11179
11180 // Details for Fonts
11181#ifndef IMGUI_DISABLE_DEMO_WINDOWS
11182 ImFontAtlas* atlas = g.IO.Fonts;
11183 if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size))
11184 {
11185 ShowFontAtlas(atlas);
11186 TreePop();
11187 }
11188#endif
11189
11190 // Details for Docking
11191#ifdef IMGUI_HAS_DOCK
11192 if (TreeNode("Docking"))
11193 {
11194 TreePop();
11195 }
11196#endif // #ifdef IMGUI_HAS_DOCK
11197
11198 // Settings
11199 if (TreeNode("Settings"))
11200 {
11201 if (SmallButton("Clear"))
11202 ClearIniSettings();
11203 SameLine();
11204 if (SmallButton("Save to memory"))
11205 SaveIniSettingsToMemory();
11206 SameLine();
11207 if (SmallButton("Save to disk"))
11208 SaveIniSettingsToDisk(g.IO.IniFilename);
11209 SameLine();
11210 if (g.IO.IniFilename)
11211 Text("\"%s\"", g.IO.IniFilename);
11212 else
11213 TextUnformatted("<NULL>");
11214 Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
11215 if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
11216 {
11217 for (int n = 0; n < g.SettingsHandlers.Size; n++)
11218 BulletText("%s", g.SettingsHandlers[n].TypeName);
11219 TreePop();
11220 }
11221 if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
11222 {
11223 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
11224 DebugNodeWindowSettings(settings);
11225 TreePop();
11226 }
11227
11228 if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
11229 {
11230 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
11231 DebugNodeTableSettings(settings);
11232 TreePop();
11233 }
11234
11235#ifdef IMGUI_HAS_DOCK
11236#endif // #ifdef IMGUI_HAS_DOCK
11237
11238 if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
11239 {
11240 InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
11241 TreePop();
11242 }
11243 TreePop();
11244 }
11245
11246 // Misc Details
11247 if (TreeNode("Internal state"))
11248 {
11249 const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
11250
11251 Text("WINDOWING");
11252 Indent();
11253 Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
11254 Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
11255 Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
11256 Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
11257 Unindent();
11258
11259 Text("ITEMS");
11260 Indent();
11261 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]);
11262 Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
11263 Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask);
11264 Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
11265 Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
11266 Unindent();
11267
11268 Text("NAV,FOCUS");
11269 Indent();
11270 Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
11271 Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
11272 Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
11273 Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
11274 Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
11275 Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
11276 Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
11277 Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
11278 Unindent();
11279
11280 TreePop();
11281 }
11282
11283 // Overlay: Display windows Rectangles and Begin Order
11284 if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
11285 {
11286 for (int n = 0; n < g.Windows.Size; n++)
11287 {
11288 ImGuiWindow* window = g.Windows[n];
11289 if (!window->WasActive)
11290 continue;
11291 ImDrawList* draw_list = GetForegroundDrawList(window);
11292 if (cfg->ShowWindowsRects)
11293 {
11294 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
11295 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
11296 }
11297 if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
11298 {
11299 char buf[32];
11300 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
11301 float font_size = GetFontSize();
11302 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
11303 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
11304 }
11305 }
11306 }
11307
11308 // Overlay: Display Tables Rectangles
11309 if (cfg->ShowTablesRects)
11310 {
11311 for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
11312 {
11313 ImGuiTable* table = g.Tables.TryGetMapData(table_n);
11314 if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
11315 continue;
11316 ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
11317 if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
11318 {
11319 for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
11320 {
11321 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
11322 ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
11323 float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
11324 draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
11325 }
11326 }
11327 else
11328 {
11329 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
11330 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
11331 }
11332 }
11333 }
11334
11335#ifdef IMGUI_HAS_DOCK
11336 // Overlay: Display Docking info
11337 if (show_docking_nodes && g.IO.KeyCtrl)
11338 {
11339 }
11340#endif // #ifdef IMGUI_HAS_DOCK
11341
11342 End();
11343}
11344
11345// [DEBUG] List fonts in a font atlas and display its texture
11346void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
11347{
11348 for (int i = 0; i < atlas->Fonts.Size; i++)
11349 {
11350 ImFont* font = atlas->Fonts[i];
11351 PushID(font);
11352 DebugNodeFont(font);
11353 PopID();
11354 }
11355 if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
11356 {
11357 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
11358 ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
11359 Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);
11360 TreePop();
11361 }
11362}
11363
11364// [DEBUG] Display contents of Columns
11365void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
11366{
11367 if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
11368 return;
11369 BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
11370 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
11371 BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
11372 TreePop();
11373}
11374
11375// [DEBUG] Display contents of ImDrawList
11376void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label)
11377{
11378 ImGuiContext& g = *GImGui;
11379 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
11380 int cmd_count = draw_list->CmdBuffer.Size;
11381 if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
11382 cmd_count--;
11383 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);
11384 if (draw_list == GetWindowDrawList())
11385 {
11386 SameLine();
11387 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)
11388 if (node_open)
11389 TreePop();
11390 return;
11391 }
11392
11393 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
11394 if (window && IsItemHovered())
11395 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11396 if (!node_open)
11397 return;
11398
11399 if (window && !window->WasActive)
11400 TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
11401
11402 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
11403 {
11404 if (pcmd->UserCallback)
11405 {
11406 BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
11407 continue;
11408 }
11409
11410 char buf[300];
11411 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
11412 pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
11413 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
11414 bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
11415 if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
11416 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
11417 if (!pcmd_node_open)
11418 continue;
11419
11420 // Calculate approximate coverage area (touched pixel count)
11421 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
11422 const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
11423 const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
11424 float total_area = 0.0f;
11425 for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
11426 {
11427 ImVec2 triangle[3];
11428 for (int n = 0; n < 3; n++, idx_n++)
11429 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
11430 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
11431 }
11432
11433 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
11434 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
11435 Selectable(buf);
11436 if (IsItemHovered() && fg_draw_list)
11437 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
11438
11439 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
11440 ImGuiListClipper clipper;
11441 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
11442 while (clipper.Step())
11443 for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
11444 {
11445 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
11446 ImVec2 triangle[3];
11447 for (int n = 0; n < 3; n++, idx_i++)
11448 {
11449 const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
11450 triangle[n] = v.pos;
11451 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
11452 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
11453 }
11454
11455 Selectable(buf, false);
11456 if (fg_draw_list && IsItemHovered())
11457 {
11458 ImDrawListFlags backup_flags = fg_draw_list->Flags;
11459 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
11460 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
11461 fg_draw_list->Flags = backup_flags;
11462 }
11463 }
11464 TreePop();
11465 }
11466 TreePop();
11467}
11468
11469// [DEBUG] Display mesh/aabb of a ImDrawCmd
11470void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
11471{
11472 IM_ASSERT(show_mesh || show_aabb);
11473 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
11474 ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
11475
11476 // Draw wire-frame version of all triangles
11477 ImRect clip_rect = draw_cmd->ClipRect;
11478 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
11479 ImDrawListFlags backup_flags = out_draw_list->Flags;
11480 out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
11481 for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; )
11482 {
11483 ImVec2 triangle[3];
11484 for (int n = 0; n < 3; n++, idx_n++)
11485 vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
11486 if (show_mesh)
11487 out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
11488 }
11489 // Draw bounding boxes
11490 if (show_aabb)
11491 {
11492 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
11493 out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
11494 }
11495 out_draw_list->Flags = backup_flags;
11496}
11497
11498// [DEBUG] Display details for a single font, called by ShowStyleEditor().
11499void ImGui::DebugNodeFont(ImFont* font)
11500{
11501 bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)",
11502 font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);
11503 SameLine();
11504 if (SmallButton("Set as default"))
11505 GetIO().FontDefault = font;
11506 if (!opened)
11507 return;
11508
11509 // Display preview text
11510 PushFont(font);
11511 Text("The quick brown fox jumps over the lazy dog");
11512 PopFont();
11513
11514 // Display details
11515 SetNextItemWidth(GetFontSize() * 8);
11516 DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
11517 SameLine(); MetricsHelpMarker(
11518 "Note than the default embedded font is NOT meant to be scaled.\n\n"
11519 "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
11520 "You may oversample them to get some flexibility with scaling. "
11521 "You can also render at multiple sizes and select which one to use at runtime.\n\n"
11522 "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");
11523 Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
11524 char c_str[5];
11525 Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);
11526 Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);
11527 const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);
11528 Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);
11529 for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
11530 if (font->ConfigData)
11531 if (const ImFontConfig* cfg = &font->ConfigData[config_i])
11532 BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
11533 config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);
11534
11535 // Display all glyphs of the fonts in separate pages of 256 characters
11536 if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size))
11537 {
11538 ImDrawList* draw_list = GetWindowDrawList();
11539 const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
11540 const float cell_size = font->FontSize * 1;
11541 const float cell_spacing = GetStyle().ItemSpacing.y;
11542 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
11543 {
11544 // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
11545 // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
11546 // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
11547 if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))
11548 {
11549 base += 4096 - 256;
11550 continue;
11551 }
11552
11553 int count = 0;
11554 for (unsigned int n = 0; n < 256; n++)
11555 if (font->FindGlyphNoFallback((ImWchar)(base + n)))
11556 count++;
11557 if (count <= 0)
11558 continue;
11559 if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
11560 continue;
11561
11562 // Draw a 16x16 grid of glyphs
11563 ImVec2 base_pos = GetCursorScreenPos();
11564 for (unsigned int n = 0; n < 256; n++)
11565 {
11566 // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
11567 // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
11568 ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
11569 ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
11570 const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));
11571 draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
11572 if (glyph)
11573 font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
11574 if (glyph && IsMouseHoveringRect(cell_p1, cell_p2))
11575 {
11576 BeginTooltip();
11577 Text("Codepoint: U+%04X", base + n);
11578 Separator();
11579 Text("Visible: %d", glyph->Visible);
11580 Text("AdvanceX: %.1f", glyph->AdvanceX);
11581 Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
11582 Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
11583 EndTooltip();
11584 }
11585 }
11586 Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
11587 TreePop();
11588 }
11589 TreePop();
11590 }
11591 TreePop();
11592}
11593
11594// [DEBUG] Display contents of ImGuiStorage
11595void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
11596{
11597 if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
11598 return;
11599 for (int n = 0; n < storage->Data.Size; n++)
11600 {
11601 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
11602 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.
11603 }
11604 TreePop();
11605}
11606
11607// [DEBUG] Display contents of ImGuiTabBar
11608void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
11609{
11610 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
11611 char buf[256];
11612 char* p = buf;
11613 const char* buf_end = buf + IM_ARRAYSIZE(buf);
11614 const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
11615 p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
11616 p += ImFormatString(p, buf_end - p, " { ");
11617 for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
11618 {
11619 ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11620 p += ImFormatString(p, buf_end - p, "%s'%s'",
11621 tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???");
11622 }
11623 p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
11624 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11625 bool open = TreeNode(label, "%s", buf);
11626 if (!is_active) { PopStyleColor(); }
11627 if (is_active && IsItemHovered())
11628 {
11629 ImDrawList* draw_list = GetForegroundDrawList();
11630 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
11631 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));
11632 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));
11633 }
11634 if (open)
11635 {
11636 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
11637 {
11638 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
11639 PushID(tab);
11640 if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
11641 if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
11642 Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f",
11643 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth);
11644 PopID();
11645 }
11646 TreePop();
11647 }
11648}
11649
11650void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
11651{
11652 SetNextItemOpen(true, ImGuiCond_Once);
11653 if (TreeNode("viewport0", "Viewport #%d", 0))
11654 {
11655 ImGuiWindowFlags flags = viewport->Flags;
11656 BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
11657 viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
11658 viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y);
11659 BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
11660 (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "",
11661 (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
11662 (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "");
11663 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++)
11664 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++)
11665 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList");
11666 TreePop();
11667 }
11668}
11669
11670void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
11671{
11672 if (window == NULL)
11673 {
11674 BulletText("%s: NULL", label);
11675 return;
11676 }
11677
11678 ImGuiContext& g = *GImGui;
11679 const bool is_active = window->WasActive;
11680 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
11681 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
11682 const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
11683 if (!is_active) { PopStyleColor(); }
11684 if (IsItemHovered() && is_active)
11685 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
11686 if (!open)
11687 return;
11688
11689 if (window->MemoryCompacted)
11690 TextDisabled("Note: some memory buffers have been compacted/freed.");
11691
11692 ImGuiWindowFlags flags = window->Flags;
11693 DebugNodeDrawList(window, window->DrawList, "DrawList");
11694 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);
11695 BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
11696 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
11697 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
11698 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
11699 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" : "");
11700 BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
11701 BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
11702 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
11703 {
11704 ImRect r = window->NavRectRel[layer];
11705 if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)
11706 {
11707 BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
11708 continue;
11709 }
11710 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);
11711 if (IsItemHovered())
11712 GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255));
11713 }
11714 BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
11715 if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); }
11716 if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
11717 if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
11718 if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
11719 {
11720 for (int n = 0; n < window->ColumnsStorage.Size; n++)
11721 DebugNodeColumns(&window->ColumnsStorage[n]);
11722 TreePop();
11723 }
11724 DebugNodeStorage(&window->StateStorage, "Storage");
11725 TreePop();
11726}
11727
11728void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
11729{
11730 Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
11731 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
11732}
11733
11734void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
11735{
11736 if (!TreeNode(label, "%s (%d)", label, windows->Size))
11737 return;
11738 Text("(In front-to-back order:)");
11739 for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
11740 {
11741 PushID((*windows)[i]);
11742 DebugNodeWindow((*windows)[i], "Window");
11743 PopID();
11744 }
11745 TreePop();
11746}
11747
11748#else
11749
11750void ImGui::ShowMetricsWindow(bool*) {}
11751void ImGui::ShowFontAtlas(ImFontAtlas*) {}
11752void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
11753void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {}
11754void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
11755void ImGui::DebugNodeFont(ImFont*) {}
11756void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
11757void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
11758void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
11759void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
11760void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
11761void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
11762
11763#endif
11764
11765//-----------------------------------------------------------------------------
11766
11767// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
11768// 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.
11769#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
11770#include "imgui_user.inl"
11771#endif
11772
11773//-----------------------------------------------------------------------------
11774
11775#endif // #ifndef IMGUI_DISABLE
11776