1 // dear imgui, v1.79 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/3075 (please post your screenshots/video there!)
15 // - Glossary https://github.com/ocornut/imgui/wiki/Glossary
16 // - Wiki https://github.com/ocornut/imgui/wiki
17 // - Issues & support https://github.com/ocornut/imgui/issues
18
19 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
20 // See LICENSE.txt for copyright and licensing details (standard MIT License).
21 // This library is free but needs your support to sustain development and maintenance.
22 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
23 // Individuals: you can support continued development via donations. See docs/README or web page.
24
25 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
26 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
27 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
28 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
29 // to a better solution or official support for them.
30
31 /*
32
33 Index of this file:
34
35 DOCUMENTATION
36
37 - MISSION STATEMENT
38 - END-USER GUIDE
39 - PROGRAMMER GUIDE
40 - READ FIRST
41 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
42 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
43 - HOW A SIMPLE APPLICATION MAY LOOK LIKE
44 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
45 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
46 - API BREAKING CHANGES (read me when you update!)
47 - FREQUENTLY ASKED QUESTIONS (FAQ)
48 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
49
50 CODE
51 (search for "[SECTION]" in the code to find them)
52
53 // [SECTION] INCLUDES
54 // [SECTION] FORWARD DECLARATIONS
55 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
56 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
57 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
58 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
59 // [SECTION] MISC HELPERS/UTILITIES (File functions)
60 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
61 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
62 // [SECTION] ImGuiStorage
63 // [SECTION] ImGuiTextFilter
64 // [SECTION] ImGuiTextBuffer
65 // [SECTION] ImGuiListClipper
66 // [SECTION] STYLING
67 // [SECTION] RENDER HELPERS
68 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
69 // [SECTION] ERROR CHECKING
70 // [SECTION] LAYOUT
71 // [SECTION] SCROLLING
72 // [SECTION] TOOLTIPS
73 // [SECTION] POPUPS
74 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
75 // [SECTION] DRAG AND DROP
76 // [SECTION] LOGGING/CAPTURING
77 // [SECTION] SETTINGS
78 // [SECTION] PLATFORM DEPENDENT HELPERS
79 // [SECTION] METRICS/DEBUG WINDOW
80
81 */
82
83 //-----------------------------------------------------------------------------
84 // DOCUMENTATION
85 //-----------------------------------------------------------------------------
86
87 /*
88
89 MISSION STATEMENT
90 =================
91
92 - Easy to use to create code-driven and data-driven tools.
93 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
94 - Easy to hack and improve.
95 - Minimize screen real-estate usage.
96 - Minimize setup and maintenance.
97 - Minimize state storage on user side.
98 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
99 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
100 opening a tree node for the first time, etc. but a typical frame should not allocate anything).
101
102 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
103 - Doesn't look fancy, doesn't animate.
104 - Limited layout features, intricate layouts are typically crafted in code.
105
106
107 END-USER GUIDE
108 ==============
109
110 - Double-click on title bar to collapse window.
111 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
112 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
113 - Click and drag on any empty space to move window.
114 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
115 - CTRL+Click on a slider or drag box to input value as text.
116 - Use mouse wheel to scroll.
117 - Text editor:
118 - Hold SHIFT or use mouse to select text.
119 - CTRL+Left/Right to word jump.
120 - CTRL+Shift+Left/Right to select words.
121 - CTRL+A our Double-Click to select all.
122 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
123 - CTRL+Z,CTRL+Y to undo/redo.
124 - ESCAPE to revert text to its original value.
125 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
126 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
127 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
128 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
129
130
131 PROGRAMMER GUIDE
132 ================
133
134 READ FIRST
135 ----------
136 - Remember to read the FAQ (https://www.dearimgui.org/faq)
137 - 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
138 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
139 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
140 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
141 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
142 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ.
143 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
144 For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI,
145 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
146 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
147 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
148 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
149 If you get an assert, read the messages and comments around the assert.
150 - 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.
151 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
152 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
153 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
154 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
155
156
157 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
158 ----------------------------------------------
159 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
160 - Or maintain your own branch where you have imconfig.h modified.
161 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
162 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
163 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
164 likely be a comment about it. Please report any issue to the GitHub page!
165 - Try to keep your copy of dear imgui reasonably up to date.
166
167
168 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
169 ---------------------------------------------------------------
170 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
171 - In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder.
172 - Add the Dear ImGui source files + selected back-end source files to your projects or using your preferred build system.
173 It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
174 - 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.
175 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
176 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
177 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
178 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
179 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
180 - 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.
181
182
183 HOW A SIMPLE APPLICATION MAY LOOK LIKE
184 --------------------------------------
185 EXHIBIT 1: USING THE EXAMPLE BINDINGS (= imgui_impl_XXX.cpp files from the examples/ folder).
186 The sub-folders in examples/ contains examples applications following this structure.
187
188 // Application init: create a dear imgui context, setup some options, load fonts
189 ImGui::CreateContext();
190 ImGuiIO& io = ImGui::GetIO();
191 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
192 // TODO: Fill optional fields of the io structure later.
193 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
194
195 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
196 ImGui_ImplWin32_Init(hwnd);
197 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
198
199 // Application main loop
200 while (true)
201 {
202 // Feed inputs to dear imgui, start new frame
203 ImGui_ImplDX11_NewFrame();
204 ImGui_ImplWin32_NewFrame();
205 ImGui::NewFrame();
206
207 // Any application code here
208 ImGui::Text("Hello, world!");
209
210 // Render dear imgui into screen
211 ImGui::Render();
212 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
213 g_pSwapChain->Present(1, 0);
214 }
215
216 // Shutdown
217 ImGui_ImplDX11_Shutdown();
218 ImGui_ImplWin32_Shutdown();
219 ImGui::DestroyContext();
220
221 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
222
223 // Application init: create a dear imgui context, setup some options, load fonts
224 ImGui::CreateContext();
225 ImGuiIO& io = ImGui::GetIO();
226 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
227 // TODO: Fill optional fields of the io structure later.
228 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
229
230 // Build and load the texture atlas into a texture
231 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
232 int width, height;
233 unsigned char* pixels = NULL;
234 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
235
236 // At this point you've got the texture data and you need to upload that your your graphic system:
237 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
238 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
239 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
240 io.Fonts->TexID = (void*)texture;
241
242 // Application main loop
243 while (true)
244 {
245 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
246 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
247 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
248 io.DisplaySize.x = 1920.0f; // set the current display width
249 io.DisplaySize.y = 1280.0f; // set the current display height here
250 io.MousePos = my_mouse_pos; // set the mouse position
251 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
252 io.MouseDown[1] = my_mouse_buttons[1];
253
254 // Call NewFrame(), after this point you can use ImGui::* functions anytime
255 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
256 ImGui::NewFrame();
257
258 // Most of your application code here
259 ImGui::Text("Hello, world!");
260 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
261 MyGameRender(); // may use any Dear ImGui functions as well!
262
263 // Render dear imgui, swap buffers
264 // (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)
265 ImGui::EndFrame();
266 ImGui::Render();
267 ImDrawData* draw_data = ImGui::GetDrawData();
268 MyImGuiRenderFunction(draw_data);
269 SwapBuffers();
270 }
271
272 // Shutdown
273 ImGui::DestroyContext();
274
275 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application,
276 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
277 Please read the FAQ and example applications for details about this!
278
279
280 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
281 ---------------------------------------------
282 The bindings in impl_impl_XXX.cpp files contains many working implementations of a rendering function.
283
284 void void MyImGuiRenderFunction(ImDrawData* draw_data)
285 {
286 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
287 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
288 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
289 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
290 for (int n = 0; n < draw_data->CmdListsCount; n++)
291 {
292 const ImDrawList* cmd_list = draw_data->CmdLists[n];
293 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
294 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
295 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
296 {
297 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
298 if (pcmd->UserCallback)
299 {
300 pcmd->UserCallback(cmd_list, pcmd);
301 }
302 else
303 {
304 // The texture for the draw call is specified by pcmd->TextureId.
305 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
306 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
307
308 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
309 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
310 // (some elements visible outside their bounds) but you can fix that once everything else works!
311 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
312 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
313 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
314 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
315 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
316 ImVec2 pos = draw_data->DisplayPos;
317 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));
318
319 // Render 'pcmd->ElemCount/3' indexed triangles.
320 // 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.
321 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
322 }
323 idx_buffer += pcmd->ElemCount;
324 }
325 }
326 }
327
328
329 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
330 ------------------------------------------
331 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
332 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
333 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
334 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
335 - Keyboard:
336 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
337 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
338 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
339 will be set. For more advanced uses, you may want to read from:
340 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
341 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
342 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
343 Please reach out if you think the game vs navigation input sharing could be improved.
344 - Gamepad:
345 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
346 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
347 Note that io.NavInputs[] is cleared by EndFrame().
348 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
349 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
350 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
351 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.).
352 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
353 - 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
354 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
355 - Mouse:
356 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
357 - 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.
358 - 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.
359 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
360 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.
361 When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that.
362 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!)
363 (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
364 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
365
366
367 API BREAKING CHANGES
368 ====================
369
370 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
371 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.
372 When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
373 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
374
375 - 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().
376 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
377 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
378 - if you omitted the 'power' parameter (likely!), you are not affected.
379 - 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.
380 - 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.
381 see https://github.com/ocornut/imgui/issues/3361 for all details.
382 kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version were removed directly as they were most unlikely ever used.
383 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
384 - 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.
385 - 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.
386 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete).
387 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
388 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
389 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
390 - 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.
391 - 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.
392 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
393 - ShowTestWindow() -> use ShowDemoWindow()
394 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
395 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
396 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
397 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
398 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
399 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
400 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
401 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
402 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API.
403 - 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).
404 - 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.
405 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
406 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
407 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
408 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
409 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
410 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
411 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
412 - ImFont::Glyph -> use ImFontGlyph
413 - 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.
414 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.
415 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).
416 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
417 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
418 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
419 - 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.
420 - 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
421 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.
422 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.
423 Please reach out if you are affected.
424 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
425 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
426 - 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.
427 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
428 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
429 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
430 - 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 arbitrary small value!
431 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
432 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
433 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
434 - 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.
435 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
436 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
437 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
438 - 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.
439 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
440 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
441 - 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.
442 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
443 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
444 - 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).
445 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
446 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
447 - 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.
448 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
449 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
450 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
451 - 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.).
452 old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
453 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
454 in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
455 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
456 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
457 - 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.
458 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
459 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.
460 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.
461 - 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",
462 consistent with other functions. Kept redirection functions (will obsolete).
463 - 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.
464 - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch).
465 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
466 - 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.
467 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
468 - 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.
469 - 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.
470 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
471 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
472 - removed Shutdown() function, as DestroyContext() serve this purpose.
473 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
474 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
475 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
476 - 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.
477 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
478 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
479 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
480 - 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.
481 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
482 - 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
483 - 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.
484 - 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.
485 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
486 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
487 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
488 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
489 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
490 - 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.
491 - 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.
492 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.
493 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
494 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
495 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
496 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
497 - 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.
498 - 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.
499 - 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.
500 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
501 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
502 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
503 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
504 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
505 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
506 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
507 - 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).
508 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
509 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
510 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
511 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
512 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
513 - 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.
514 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
515 - 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.
516 - 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).
517 - 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).
518 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
519 - 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.
520 - 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.
521 - 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))'
522 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
523 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
524 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
525 - 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().
526 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
527 - 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.
528 - 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.
529 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
530 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 tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
531 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:
532 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); }
533 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.
534 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
535 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
536 - 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).
537 - 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.
538 - 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).
539 - 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)
540 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
541 - 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.
542 - 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.
543 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
544 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
545 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
546 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.
547 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!
548 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
549 - 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.
550 - 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
551 - 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.
552 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
553 - 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.
554 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
555 - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
556 - the signature of the io.RenderDrawListsFn handler has changed!
557 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
558 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
559 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
560 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
561 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
562 - 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.
563 - 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!
564 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
565 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
566 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
567 - 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.
568 - 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
569 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry!
570 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
571 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
572 - 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.
573 - 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.
574 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
575 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
576 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
577 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
578 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
579 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
580 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
581 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
582 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
583 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
584 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
585 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
586 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
587 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
588 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
589 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
590 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
591 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
592 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
593 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
594 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.
595 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
596 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
597 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
598 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
599 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
600 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
601 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
602 - 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)
603 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
604 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
605 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
606 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
607 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
608 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
609
610
611 FREQUENTLY ASKED QUESTIONS (FAQ)
612 ================================
613
614 Read all answers online:
615 https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
616 Read all answers locally (with a text editor or ideally a Markdown viewer):
617 docs/FAQ.md
618 Some answers are copied down here to facilitate searching in code.
619
620 Q&A: Basics
621 ===========
622
623 Q: Where is the documentation?
624 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
625 - Run the examples/ and explore them.
626 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
627 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
628 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
629 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
630 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
631 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
632 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
633 - Your programming IDE is your friend, find the type or function declaration to find comments
634 associated to it.
635
636 Q: What is this library called?
637 Q: Which version should I get?
638 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
639 >> See https://www.dearimgui.org/faq for details.
640
641 Q&A: Integration
642 ================
643
644 Q: How to get started?
645 A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
646
647 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
648 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
649 >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
650
651 Q. How can I enable keyboard controls?
652 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
653 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text..
654 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
655 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries..
656 >> See https://www.dearimgui.org/faq
657
658 Q&A: Usage
659 ----------
660
661 Q: Why is my widget not reacting when I click on it?
662 Q: How can I have widgets with an empty label?
663 Q: How can I have multiple widgets with the same label?
664 Q: How can I display an image? What is ImTextureID, how does it works?
665 Q: How can I use my own math types instead of ImVec2/ImVec4?
666 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
667 Q: How can I display custom shapes? (using low-level ImDrawList API)
668 >> See https://www.dearimgui.org/faq
669
670 Q&A: Fonts, Text
671 ================
672
673 Q: How should I handle DPI in my application?
674 Q: How can I load a different font than the default?
675 Q: How can I easily use icons in my application?
676 Q: How can I load multiple fonts?
677 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
678 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md
679
680 Q&A: Concerns
681 =============
682
683 Q: Who uses Dear ImGui?
684 Q: Can you create elaborate/serious tools with Dear ImGui?
685 Q: Can you reskin the look of Dear ImGui?
686 Q: Why using C++ (as opposed to C)?
687 >> See https://www.dearimgui.org/faq
688
689 Q&A: Community
690 ==============
691
692 Q: How can I help?
693 A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
694 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
695 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.
696 - Individuals: you can support continued development via PayPal donations. See README.
697 - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
698 and see how you want to help and can help!
699 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
700 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3075). Visuals are ideal as they inspire other programmers.
701 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
702 - 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).
703
704 */
705
706 //-------------------------------------------------------------------------
707 // [SECTION] INCLUDES
708 //-------------------------------------------------------------------------
709
710 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
711 #define _CRT_SECURE_NO_WARNINGS
712 #endif
713
714 #include "imgui.h"
715 #ifndef IMGUI_DISABLE
716
717 #ifndef IMGUI_DEFINE_MATH_OPERATORS
718 #define IMGUI_DEFINE_MATH_OPERATORS
719 #endif
720 #include "imgui_internal.h"
721
722 // System includes
723 #include <ctype.h> // toupper
724 #include <stdio.h> // vsnprintf, sscanf, printf
725 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
726 #include <stddef.h> // intptr_t
727 #else
728 #include <stdint.h> // intptr_t
729 #endif
730
731 // [Windows] OS specific includes (optional)
732 #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)
733 #define IMGUI_DISABLE_WIN32_FUNCTIONS
734 #endif
735 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
736 #ifndef WIN32_LEAN_AND_MEAN
737 #define WIN32_LEAN_AND_MEAN
738 #endif
739 #ifndef NOMINMAX
740 #define NOMINMAX
741 #endif
742 #ifndef __MINGW32__
743 #include <Windows.h> // _wfopen, OpenClipboard
744 #else
745 #include <windows.h>
746 #endif
747 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions
748 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
749 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
750 #endif
751 #endif
752
753 // [Apple] OS specific includes
754 #if defined(__APPLE__)
755 #include <TargetConditionals.h>
756 #endif
757
758 // Visual Studio warnings
759 #ifdef _MSC_VER
760 #pragma warning (disable: 4127) // condition expression is constant
761 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
762 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
763 #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
764 #endif
765 #endif
766
767 // Clang/GCC warnings with -Weverything
768 #if defined(__clang__)
769 #if __has_warning("-Wunknown-warning-option")
770 #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!
771 #endif
772 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
773 #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
774 #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.
775 #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.
776 #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.
777 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is.
778 #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
779 #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.
780 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int'
781 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
782 #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.
783 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
784 #elif defined(__GNUC__)
785 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
786 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
787 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
788 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
789 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
790 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
791 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
792 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
793 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
794 #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
795 #endif
796
797 // Debug options
798 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
799 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
800 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
801
802 // 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.
803 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
804 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
805
806 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
807 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
808 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
809 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
810
811 //-------------------------------------------------------------------------
812 // [SECTION] FORWARD DECLARATIONS
813 //-------------------------------------------------------------------------
814
815 static void SetCurrentWindow(ImGuiWindow* window);
816 static void FindHoveredWindow();
817 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags);
818 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
819
820 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
821 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
822
823 static ImRect GetViewportRect();
824
825 // Settings
826 static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
827 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
828 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
829 static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
830 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
831
832 // Platform Dependents default implementation for IO functions
833 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
834 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
835 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
836
837 namespace ImGui
838 {
839 // Navigation
840 static void NavUpdate();
841 static void NavUpdateWindowing();
842 static void NavUpdateWindowingOverlay();
843 static void NavUpdateMoveResult();
844 static void NavUpdateInitResult();
845 static float NavUpdatePageUpPageDown();
846 static inline void NavUpdateAnyRequestFlag();
847 static void NavEndFrame();
848 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
849 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
850 static ImVec2 NavCalcPreferredRefPos();
851 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
852 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
853 static int FindWindowFocusIndex(ImGuiWindow* window);
854
855 // Error Checking
856 static void ErrorCheckNewFrameSanityChecks();
857 static void ErrorCheckEndFrameSanityChecks();
858 static void ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write);
859
860 // Misc
861 static void UpdateSettings();
862 static void UpdateMouseInputs();
863 static void UpdateMouseWheel();
864 static void UpdateTabFocus();
865 static void UpdateDebugToolItemPicker();
866 static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
867 static void RenderWindowOuterBorders(ImGuiWindow* window);
868 static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
869 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
870
871 }
872
873 //-----------------------------------------------------------------------------
874 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
875 //-----------------------------------------------------------------------------
876
877 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
878 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
879 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
880 // SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
881 // In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
882 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
883 // If you want thread-safety to allow N threads to access N different contexts, you can:
884 // - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
885 // struct ImGuiContext;
886 // extern thread_local ImGuiContext* MyImGuiTLS;
887 // #define GImGui MyImGuiTLS
888 // And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
889 // - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
890 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
891 #ifndef GImGui
892 ImGuiContext* GImGui = NULL;
893 #endif
894
895 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
896 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
897 // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
898 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)899 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)900 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
901 #else
MallocWrapper(size_t size,void * user_data)902 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)903 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
904 #endif
905
906 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
907 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
908 static void* GImAllocatorUserData = NULL;
909
910 //-----------------------------------------------------------------------------
911 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
912 //-----------------------------------------------------------------------------
913
ImGuiStyle()914 ImGuiStyle::ImGuiStyle()
915 {
916 Alpha = 1.0f; // Global alpha applies to everything in ImGui
917 WindowPadding = ImVec2(8,8); // Padding within a window
918 WindowRounding = 7.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.
919 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
920 WindowMinSize = ImVec2(32,32); // Minimum window size
921 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
922 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
923 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
924 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
925 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
926 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
927 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
928 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
929 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
930 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
931 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
932 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!
933 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
934 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
935 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
936 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
937 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
938 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
939 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
940 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
941 TabBorderSize = 0.0f; // Thickness of border around tabs.
942 TabMinWidthForUnselectedCloseButton = 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.
943 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
944 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
945 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.
946 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.
947 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.
948 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
949 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
950 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require back-end to render with bilinear filtering.
951 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
952 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.
953 CircleSegmentMaxError = 1.60f; // 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.
954
955 // Default theme
956 ImGui::StyleColorsDark(this);
957 }
958
959 // 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.
960 // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
ScaleAllSizes(float scale_factor)961 void ImGuiStyle::ScaleAllSizes(float scale_factor)
962 {
963 WindowPadding = ImFloor(WindowPadding * scale_factor);
964 WindowRounding = ImFloor(WindowRounding * scale_factor);
965 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
966 ChildRounding = ImFloor(ChildRounding * scale_factor);
967 PopupRounding = ImFloor(PopupRounding * scale_factor);
968 FramePadding = ImFloor(FramePadding * scale_factor);
969 FrameRounding = ImFloor(FrameRounding * scale_factor);
970 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
971 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
972 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
973 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
974 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
975 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
976 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
977 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
978 GrabRounding = ImFloor(GrabRounding * scale_factor);
979 LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor);
980 TabRounding = ImFloor(TabRounding * scale_factor);
981 if (TabMinWidthForUnselectedCloseButton != FLT_MAX)
982 TabMinWidthForUnselectedCloseButton = ImFloor(TabMinWidthForUnselectedCloseButton * scale_factor);
983 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
984 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
985 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
986 }
987
ImGuiIO()988 ImGuiIO::ImGuiIO()
989 {
990 // Most fields are initialized with zero
991 memset(this, 0, sizeof(*this));
992 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.
993
994 // Settings
995 ConfigFlags = ImGuiConfigFlags_None;
996 BackendFlags = ImGuiBackendFlags_None;
997 DisplaySize = ImVec2(-1.0f, -1.0f);
998 DeltaTime = 1.0f / 60.0f;
999 IniSavingRate = 5.0f;
1000 IniFilename = "imgui.ini";
1001 LogFilename = "imgui_log.txt";
1002 MouseDoubleClickTime = 0.30f;
1003 MouseDoubleClickMaxDist = 6.0f;
1004 for (int i = 0; i < ImGuiKey_COUNT; i++)
1005 KeyMap[i] = -1;
1006 KeyRepeatDelay = 0.275f;
1007 KeyRepeatRate = 0.050f;
1008 UserData = NULL;
1009
1010 Fonts = NULL;
1011 FontGlobalScale = 1.0f;
1012 FontDefault = NULL;
1013 FontAllowUserScaling = false;
1014 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1015
1016 // Miscellaneous options
1017 MouseDrawCursor = false;
1018 #ifdef __APPLE__
1019 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1020 #else
1021 ConfigMacOSXBehaviors = false;
1022 #endif
1023 ConfigInputTextCursorBlink = true;
1024 ConfigWindowsResizeFromEdges = true;
1025 ConfigWindowsMoveFromTitleBarOnly = false;
1026 ConfigWindowsMemoryCompactTimer = 60.0f;
1027
1028 // Platform Functions
1029 BackendPlatformName = BackendRendererName = NULL;
1030 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1031 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1032 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1033 ClipboardUserData = NULL;
1034 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1035 ImeWindowHandle = NULL;
1036
1037 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1038 RenderDrawListsFn = NULL;
1039 #endif
1040
1041 // Input (NB: we already have memset zero the entire structure!)
1042 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1043 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1044 MouseDragThreshold = 6.0f;
1045 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1046 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1047 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1048 }
1049
1050 // Pass in translated ASCII characters for text input.
1051 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1052 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1053 void ImGuiIO::AddInputCharacter(unsigned int c)
1054 {
1055 if (c != 0)
1056 InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
1057 }
1058
1059 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1060 // we should save the high surrogate.
AddInputCharacterUTF16(ImWchar16 c)1061 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1062 {
1063 if (c == 0 && InputQueueSurrogate == 0)
1064 return;
1065
1066 if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1067 {
1068 if (InputQueueSurrogate != 0)
1069 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1070 InputQueueSurrogate = c;
1071 return;
1072 }
1073
1074 ImWchar cp = c;
1075 if (InputQueueSurrogate != 0)
1076 {
1077 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1078 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID);
1079 else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang)
1080 cp = IM_UNICODE_CODEPOINT_INVALID;
1081 else
1082 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1083 InputQueueSurrogate = 0;
1084 }
1085 InputQueueCharacters.push_back(cp);
1086 }
1087
AddInputCharactersUTF8(const char * utf8_chars)1088 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1089 {
1090 while (*utf8_chars != 0)
1091 {
1092 unsigned int c = 0;
1093 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1094 if (c != 0)
1095 InputQueueCharacters.push_back((ImWchar)c);
1096 }
1097 }
1098
ClearInputCharacters()1099 void ImGuiIO::ClearInputCharacters()
1100 {
1101 InputQueueCharacters.resize(0);
1102 }
1103
1104 //-----------------------------------------------------------------------------
1105 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1106 //-----------------------------------------------------------------------------
1107
ImBezierClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1108 ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1109 {
1110 IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1111 ImVec2 p_last = p1;
1112 ImVec2 p_closest;
1113 float p_closest_dist2 = FLT_MAX;
1114 float t_step = 1.0f / (float)num_segments;
1115 for (int i_step = 1; i_step <= num_segments; i_step++)
1116 {
1117 ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step);
1118 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1119 float dist2 = ImLengthSqr(p - p_line);
1120 if (dist2 < p_closest_dist2)
1121 {
1122 p_closest = p_line;
1123 p_closest_dist2 = dist2;
1124 }
1125 p_last = p_current;
1126 }
1127 return p_closest;
1128 }
1129
1130 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
BezierClosestPointCasteljauStep(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)1131 static void BezierClosestPointCasteljauStep(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)
1132 {
1133 float dx = x4 - x1;
1134 float dy = y4 - y1;
1135 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1136 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1137 d2 = (d2 >= 0) ? d2 : -d2;
1138 d3 = (d3 >= 0) ? d3 : -d3;
1139 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1140 {
1141 ImVec2 p_current(x4, y4);
1142 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1143 float dist2 = ImLengthSqr(p - p_line);
1144 if (dist2 < p_closest_dist2)
1145 {
1146 p_closest = p_line;
1147 p_closest_dist2 = dist2;
1148 }
1149 p_last = p_current;
1150 }
1151 else if (level < 10)
1152 {
1153 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f;
1154 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f;
1155 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f;
1156 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f;
1157 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f;
1158 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1159 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1160 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1161 }
1162 }
1163
1164 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1165 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
ImBezierClosestPointCasteljau(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,float tess_tol)1166 ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1167 {
1168 IM_ASSERT(tess_tol > 0.0f);
1169 ImVec2 p_last = p1;
1170 ImVec2 p_closest;
1171 float p_closest_dist2 = FLT_MAX;
1172 BezierClosestPointCasteljauStep(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);
1173 return p_closest;
1174 }
1175
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1176 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1177 {
1178 ImVec2 ap = p - a;
1179 ImVec2 ab_dir = b - a;
1180 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1181 if (dot < 0.0f)
1182 return a;
1183 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1184 if (dot > ab_len_sqr)
1185 return b;
1186 return a + ab_dir * dot / ab_len_sqr;
1187 }
1188
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1189 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1190 {
1191 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1192 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1193 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1194 return ((b1 == b2) && (b2 == b3));
1195 }
1196
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1197 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1198 {
1199 ImVec2 v0 = b - a;
1200 ImVec2 v1 = c - a;
1201 ImVec2 v2 = p - a;
1202 const float denom = v0.x * v1.y - v1.x * v0.y;
1203 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1204 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1205 out_u = 1.0f - out_v - out_w;
1206 }
1207
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1208 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1209 {
1210 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1211 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1212 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1213 float dist2_ab = ImLengthSqr(p - proj_ab);
1214 float dist2_bc = ImLengthSqr(p - proj_bc);
1215 float dist2_ca = ImLengthSqr(p - proj_ca);
1216 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1217 if (m == dist2_ab)
1218 return proj_ab;
1219 if (m == dist2_bc)
1220 return proj_bc;
1221 return proj_ca;
1222 }
1223
1224 //-----------------------------------------------------------------------------
1225 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1226 //-----------------------------------------------------------------------------
1227
1228 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
ImStricmp(const char * str1,const char * str2)1229 int ImStricmp(const char* str1, const char* str2)
1230 {
1231 int d;
1232 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1233 return d;
1234 }
1235
ImStrnicmp(const char * str1,const char * str2,size_t count)1236 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1237 {
1238 int d = 0;
1239 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1240 return d;
1241 }
1242
ImStrncpy(char * dst,const char * src,size_t count)1243 void ImStrncpy(char* dst, const char* src, size_t count)
1244 {
1245 if (count < 1)
1246 return;
1247 if (count > 1)
1248 strncpy(dst, src, count - 1);
1249 dst[count - 1] = 0;
1250 }
1251
ImStrdup(const char * str)1252 char* ImStrdup(const char* str)
1253 {
1254 size_t len = strlen(str);
1255 void* buf = IM_ALLOC(len + 1);
1256 return (char*)memcpy(buf, (const void*)str, len + 1);
1257 }
1258
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1259 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1260 {
1261 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1262 size_t src_size = strlen(src) + 1;
1263 if (dst_buf_size < src_size)
1264 {
1265 IM_FREE(dst);
1266 dst = (char*)IM_ALLOC(src_size);
1267 if (p_dst_size)
1268 *p_dst_size = src_size;
1269 }
1270 return (char*)memcpy(dst, (const void*)src, src_size);
1271 }
1272
ImStrchrRange(const char * str,const char * str_end,char c)1273 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1274 {
1275 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1276 return p;
1277 }
1278
ImStrlenW(const ImWchar * str)1279 int ImStrlenW(const ImWchar* str)
1280 {
1281 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1282 int n = 0;
1283 while (*str++) n++;
1284 return n;
1285 }
1286
1287 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1288 const char* ImStreolRange(const char* str, const char* str_end)
1289 {
1290 const char* p = (const char*)memchr(str, '\n', str_end - str);
1291 return p ? p : str_end;
1292 }
1293
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1294 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1295 {
1296 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1297 buf_mid_line--;
1298 return buf_mid_line;
1299 }
1300
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1301 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1302 {
1303 if (!needle_end)
1304 needle_end = needle + strlen(needle);
1305
1306 const char un0 = (char)toupper(*needle);
1307 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1308 {
1309 if (toupper(*haystack) == un0)
1310 {
1311 const char* b = needle + 1;
1312 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1313 if (toupper(*a) != toupper(*b))
1314 break;
1315 if (b == needle_end)
1316 return haystack;
1317 }
1318 haystack++;
1319 }
1320 return NULL;
1321 }
1322
1323 // Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
ImStrTrimBlanks(char * buf)1324 void ImStrTrimBlanks(char* buf)
1325 {
1326 char* p = buf;
1327 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1328 p++;
1329 char* p_start = p;
1330 while (*p != 0) // Find end of string
1331 p++;
1332 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1333 p--;
1334 if (p_start != buf) // Copy memory if we had leading blanks
1335 memmove(buf, p_start, p - p_start);
1336 buf[p - p_start] = 0; // Zero terminate
1337 }
1338
ImStrSkipBlank(const char * str)1339 const char* ImStrSkipBlank(const char* str)
1340 {
1341 while (str[0] == ' ' || str[0] == '\t')
1342 str++;
1343 return str;
1344 }
1345
1346 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1347 // 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.
1348 // B) When buf==NULL vsnprintf() will return the output size.
1349 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1350
1351 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1352 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1353 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1354 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1355 #ifdef IMGUI_USE_STB_SPRINTF
1356 #define STB_SPRINTF_IMPLEMENTATION
1357 #include "stb_sprintf.h"
1358 #endif
1359
1360 #if defined(_MSC_VER) && !defined(vsnprintf)
1361 #define vsnprintf _vsnprintf
1362 #endif
1363
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1364 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1365 {
1366 va_list args;
1367 va_start(args, fmt);
1368 #ifdef IMGUI_USE_STB_SPRINTF
1369 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1370 #else
1371 int w = vsnprintf(buf, buf_size, fmt, args);
1372 #endif
1373 va_end(args);
1374 if (buf == NULL)
1375 return w;
1376 if (w == -1 || w >= (int)buf_size)
1377 w = (int)buf_size - 1;
1378 buf[w] = 0;
1379 return w;
1380 }
1381
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1382 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1383 {
1384 #ifdef IMGUI_USE_STB_SPRINTF
1385 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1386 #else
1387 int w = vsnprintf(buf, buf_size, fmt, args);
1388 #endif
1389 if (buf == NULL)
1390 return w;
1391 if (w == -1 || w >= (int)buf_size)
1392 w = (int)buf_size - 1;
1393 buf[w] = 0;
1394 return w;
1395 }
1396 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1397
1398 // CRC32 needs a 1KB lookup table (not cache friendly)
1399 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1400 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1401 static const ImU32 GCrc32LookupTable[256] =
1402 {
1403 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1404 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1405 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1406 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1407 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1408 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1409 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1410 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1411 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1412 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1413 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1414 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1415 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1416 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1417 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1418 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1419 };
1420
1421 // Known size hash
1422 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1423 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashData(const void * data_p,size_t data_size,ImU32 seed)1424 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1425 {
1426 ImU32 crc = ~seed;
1427 const unsigned char* data = (const unsigned char*)data_p;
1428 const ImU32* crc32_lut = GCrc32LookupTable;
1429 while (data_size-- != 0)
1430 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1431 return ~crc;
1432 }
1433
1434 // Zero-terminated string hash, with support for ### to reset back to seed value
1435 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1436 // Because this syntax is rarely used we are optimizing for the common case.
1437 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1438 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1439 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHashStr(const char * data_p,size_t data_size,ImU32 seed)1440 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1441 {
1442 seed = ~seed;
1443 ImU32 crc = seed;
1444 const unsigned char* data = (const unsigned char*)data_p;
1445 const ImU32* crc32_lut = GCrc32LookupTable;
1446 if (data_size != 0)
1447 {
1448 while (data_size-- != 0)
1449 {
1450 unsigned char c = *data++;
1451 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1452 crc = seed;
1453 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1454 }
1455 }
1456 else
1457 {
1458 while (unsigned char c = *data++)
1459 {
1460 if (c == '#' && data[0] == '#' && data[1] == '#')
1461 crc = seed;
1462 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1463 }
1464 }
1465 return ~crc;
1466 }
1467
1468 //-----------------------------------------------------------------------------
1469 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1470 //-----------------------------------------------------------------------------
1471
1472 // Default file functions
1473 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1474
ImFileOpen(const char * filename,const char * mode)1475 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1476 {
1477 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1478 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1479 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
1480 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
1481 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
1482 ImVector<ImWchar> buf;
1483 buf.resize(filename_wsize + mode_wsize);
1484 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);
1485 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);
1486 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);
1487 #else
1488 return fopen(filename, mode);
1489 #endif
1490 }
1491
1492 // We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
ImFileClose(ImFileHandle f)1493 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1494 ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
ImFileRead(void * data,ImU64 sz,ImU64 count,ImFileHandle f)1495 ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); }
ImFileWrite(const void * data,ImU64 sz,ImU64 count,ImFileHandle f)1496 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
1497 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1498
1499 // Helper: Load file content into memory
1500 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
1501 // This can't really be used with "rt" because fseek size won't match read size.
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1502 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1503 {
1504 IM_ASSERT(filename && mode);
1505 if (out_file_size)
1506 *out_file_size = 0;
1507
1508 ImFileHandle f;
1509 if ((f = ImFileOpen(filename, mode)) == NULL)
1510 return NULL;
1511
1512 size_t file_size = (size_t)ImFileGetSize(f);
1513 if (file_size == (size_t)-1)
1514 {
1515 ImFileClose(f);
1516 return NULL;
1517 }
1518
1519 void* file_data = IM_ALLOC(file_size + padding_bytes);
1520 if (file_data == NULL)
1521 {
1522 ImFileClose(f);
1523 return NULL;
1524 }
1525 if (ImFileRead(file_data, 1, file_size, f) != file_size)
1526 {
1527 ImFileClose(f);
1528 IM_FREE(file_data);
1529 return NULL;
1530 }
1531 if (padding_bytes > 0)
1532 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1533
1534 ImFileClose(f);
1535 if (out_file_size)
1536 *out_file_size = file_size;
1537
1538 return file_data;
1539 }
1540
1541 //-----------------------------------------------------------------------------
1542 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1543 //-----------------------------------------------------------------------------
1544
1545 // Convert UTF-8 to 32-bit character, process single character input.
1546 // Based on stb_from_utf8() from github.com/nothings/stb/
1547 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1548 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1549 {
1550 unsigned int c = (unsigned int)-1;
1551 const unsigned char* str = (const unsigned char*)in_text;
1552 if (!(*str & 0x80))
1553 {
1554 c = (unsigned int)(*str++);
1555 *out_char = c;
1556 return 1;
1557 }
1558 if ((*str & 0xe0) == 0xc0)
1559 {
1560 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1561 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1562 if (*str < 0xc2) return 2;
1563 c = (unsigned int)((*str++ & 0x1f) << 6);
1564 if ((*str & 0xc0) != 0x80) return 2;
1565 c += (*str++ & 0x3f);
1566 *out_char = c;
1567 return 2;
1568 }
1569 if ((*str & 0xf0) == 0xe0)
1570 {
1571 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1572 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1573 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1574 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1575 c = (unsigned int)((*str++ & 0x0f) << 12);
1576 if ((*str & 0xc0) != 0x80) return 3;
1577 c += (unsigned int)((*str++ & 0x3f) << 6);
1578 if ((*str & 0xc0) != 0x80) return 3;
1579 c += (*str++ & 0x3f);
1580 *out_char = c;
1581 return 3;
1582 }
1583 if ((*str & 0xf8) == 0xf0)
1584 {
1585 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1586 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1587 if (*str > 0xf4) return 4;
1588 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1589 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1590 c = (unsigned int)((*str++ & 0x07) << 18);
1591 if ((*str & 0xc0) != 0x80) return 4;
1592 c += (unsigned int)((*str++ & 0x3f) << 12);
1593 if ((*str & 0xc0) != 0x80) return 4;
1594 c += (unsigned int)((*str++ & 0x3f) << 6);
1595 if ((*str & 0xc0) != 0x80) return 4;
1596 c += (*str++ & 0x3f);
1597 // utf-8 encodings of values used in surrogate pairs are invalid
1598 if ((c & 0xFFFFF800) == 0xD800) return 4;
1599 // If codepoint does not fit in ImWchar, use replacement character U+FFFD instead
1600 if (c > IM_UNICODE_CODEPOINT_MAX) c = IM_UNICODE_CODEPOINT_INVALID;
1601 *out_char = c;
1602 return 4;
1603 }
1604 *out_char = 0;
1605 return 0;
1606 }
1607
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1608 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1609 {
1610 ImWchar* buf_out = buf;
1611 ImWchar* buf_end = buf + buf_size;
1612 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1613 {
1614 unsigned int c;
1615 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1616 if (c == 0)
1617 break;
1618 *buf_out++ = (ImWchar)c;
1619 }
1620 *buf_out = 0;
1621 if (in_text_remaining)
1622 *in_text_remaining = in_text;
1623 return (int)(buf_out - buf);
1624 }
1625
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1626 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1627 {
1628 int char_count = 0;
1629 while ((!in_text_end || in_text < in_text_end) && *in_text)
1630 {
1631 unsigned int c;
1632 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1633 if (c == 0)
1634 break;
1635 char_count++;
1636 }
1637 return char_count;
1638 }
1639
1640 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1641 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1642 {
1643 if (c < 0x80)
1644 {
1645 buf[0] = (char)c;
1646 return 1;
1647 }
1648 if (c < 0x800)
1649 {
1650 if (buf_size < 2) return 0;
1651 buf[0] = (char)(0xc0 + (c >> 6));
1652 buf[1] = (char)(0x80 + (c & 0x3f));
1653 return 2;
1654 }
1655 if (c < 0x10000)
1656 {
1657 if (buf_size < 3) return 0;
1658 buf[0] = (char)(0xe0 + (c >> 12));
1659 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
1660 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1661 return 3;
1662 }
1663 if (c <= 0x10FFFF)
1664 {
1665 if (buf_size < 4) return 0;
1666 buf[0] = (char)(0xf0 + (c >> 18));
1667 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1668 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1669 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1670 return 4;
1671 }
1672 // Invalid code point, the max unicode is 0x10FFFF
1673 return 0;
1674 }
1675
1676 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1677 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1678 {
1679 unsigned int unused = 0;
1680 return ImTextCharFromUtf8(&unused, in_text, in_text_end);
1681 }
1682
ImTextCountUtf8BytesFromChar(unsigned int c)1683 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1684 {
1685 if (c < 0x80) return 1;
1686 if (c < 0x800) return 2;
1687 if (c < 0x10000) return 3;
1688 if (c <= 0x10FFFF) return 4;
1689 return 3;
1690 }
1691
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1692 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1693 {
1694 char* buf_out = buf;
1695 const char* buf_end = buf + buf_size;
1696 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
1697 {
1698 unsigned int c = (unsigned int)(*in_text++);
1699 if (c < 0x80)
1700 *buf_out++ = (char)c;
1701 else
1702 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c);
1703 }
1704 *buf_out = 0;
1705 return (int)(buf_out - buf);
1706 }
1707
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1708 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1709 {
1710 int bytes_count = 0;
1711 while ((!in_text_end || in_text < in_text_end) && *in_text)
1712 {
1713 unsigned int c = (unsigned int)(*in_text++);
1714 if (c < 0x80)
1715 bytes_count++;
1716 else
1717 bytes_count += ImTextCountUtf8BytesFromChar(c);
1718 }
1719 return bytes_count;
1720 }
1721
1722 //-----------------------------------------------------------------------------
1723 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
1724 // Note: The Convert functions are early design which are not consistent with other API.
1725 //-----------------------------------------------------------------------------
1726
ImAlphaBlendColors(ImU32 col_a,ImU32 col_b)1727 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
1728 {
1729 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
1730 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
1731 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
1732 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
1733 return IM_COL32(r, g, b, 0xFF);
1734 }
1735
ColorConvertU32ToFloat4(ImU32 in)1736 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1737 {
1738 float s = 1.0f / 255.0f;
1739 return ImVec4(
1740 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1741 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1742 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1743 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1744 }
1745
ColorConvertFloat4ToU32(const ImVec4 & in)1746 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1747 {
1748 ImU32 out;
1749 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1750 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1751 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1752 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1753 return out;
1754 }
1755
1756 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1757 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
ColorConvertRGBtoHSV(float r,float g,float b,float & out_h,float & out_s,float & out_v)1758 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1759 {
1760 float K = 0.f;
1761 if (g < b)
1762 {
1763 ImSwap(g, b);
1764 K = -1.f;
1765 }
1766 if (r < g)
1767 {
1768 ImSwap(r, g);
1769 K = -2.f / 6.f - K;
1770 }
1771
1772 const float chroma = r - (g < b ? g : b);
1773 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1774 out_s = chroma / (r + 1e-20f);
1775 out_v = r;
1776 }
1777
1778 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1779 // also http://en.wikipedia.org/wiki/HSL_and_HSV
ColorConvertHSVtoRGB(float h,float s,float v,float & out_r,float & out_g,float & out_b)1780 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1781 {
1782 if (s == 0.0f)
1783 {
1784 // gray
1785 out_r = out_g = out_b = v;
1786 return;
1787 }
1788
1789 h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
1790 int i = (int)h;
1791 float f = h - (float)i;
1792 float p = v * (1.0f - s);
1793 float q = v * (1.0f - s * f);
1794 float t = v * (1.0f - s * (1.0f - f));
1795
1796 switch (i)
1797 {
1798 case 0: out_r = v; out_g = t; out_b = p; break;
1799 case 1: out_r = q; out_g = v; out_b = p; break;
1800 case 2: out_r = p; out_g = v; out_b = t; break;
1801 case 3: out_r = p; out_g = q; out_b = v; break;
1802 case 4: out_r = t; out_g = p; out_b = v; break;
1803 case 5: default: out_r = v; out_g = p; out_b = q; break;
1804 }
1805 }
1806
1807 //-----------------------------------------------------------------------------
1808 // [SECTION] ImGuiStorage
1809 // Helper: Key->value storage
1810 //-----------------------------------------------------------------------------
1811
1812 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1813 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1814 {
1815 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1816 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1817 size_t count = (size_t)(last - first);
1818 while (count > 0)
1819 {
1820 size_t count2 = count >> 1;
1821 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1822 if (mid->key < key)
1823 {
1824 first = ++mid;
1825 count -= count2 + 1;
1826 }
1827 else
1828 {
1829 count = count2;
1830 }
1831 }
1832 return first;
1833 }
1834
1835 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1836 void ImGuiStorage::BuildSortByKey()
1837 {
1838 struct StaticFunc
1839 {
1840 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1841 {
1842 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1843 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1844 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1845 return 0;
1846 }
1847 };
1848 if (Data.Size > 1)
1849 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1850 }
1851
GetInt(ImGuiID key,int default_val) const1852 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1853 {
1854 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1855 if (it == Data.end() || it->key != key)
1856 return default_val;
1857 return it->val_i;
1858 }
1859
GetBool(ImGuiID key,bool default_val) const1860 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1861 {
1862 return GetInt(key, default_val ? 1 : 0) != 0;
1863 }
1864
GetFloat(ImGuiID key,float default_val) const1865 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1866 {
1867 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1868 if (it == Data.end() || it->key != key)
1869 return default_val;
1870 return it->val_f;
1871 }
1872
GetVoidPtr(ImGuiID key) const1873 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1874 {
1875 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1876 if (it == Data.end() || it->key != key)
1877 return NULL;
1878 return it->val_p;
1879 }
1880
1881 // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
GetIntRef(ImGuiID key,int default_val)1882 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1883 {
1884 ImGuiStoragePair* it = LowerBound(Data, key);
1885 if (it == Data.end() || it->key != key)
1886 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1887 return &it->val_i;
1888 }
1889
GetBoolRef(ImGuiID key,bool default_val)1890 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1891 {
1892 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1893 }
1894
GetFloatRef(ImGuiID key,float default_val)1895 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1896 {
1897 ImGuiStoragePair* it = LowerBound(Data, key);
1898 if (it == Data.end() || it->key != key)
1899 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1900 return &it->val_f;
1901 }
1902
GetVoidPtrRef(ImGuiID key,void * default_val)1903 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1904 {
1905 ImGuiStoragePair* it = LowerBound(Data, key);
1906 if (it == Data.end() || it->key != key)
1907 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1908 return &it->val_p;
1909 }
1910
1911 // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
SetInt(ImGuiID key,int val)1912 void ImGuiStorage::SetInt(ImGuiID key, int val)
1913 {
1914 ImGuiStoragePair* it = LowerBound(Data, key);
1915 if (it == Data.end() || it->key != key)
1916 {
1917 Data.insert(it, ImGuiStoragePair(key, val));
1918 return;
1919 }
1920 it->val_i = val;
1921 }
1922
SetBool(ImGuiID key,bool val)1923 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1924 {
1925 SetInt(key, val ? 1 : 0);
1926 }
1927
SetFloat(ImGuiID key,float val)1928 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1929 {
1930 ImGuiStoragePair* it = LowerBound(Data, key);
1931 if (it == Data.end() || it->key != key)
1932 {
1933 Data.insert(it, ImGuiStoragePair(key, val));
1934 return;
1935 }
1936 it->val_f = val;
1937 }
1938
SetVoidPtr(ImGuiID key,void * val)1939 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1940 {
1941 ImGuiStoragePair* it = LowerBound(Data, key);
1942 if (it == Data.end() || it->key != key)
1943 {
1944 Data.insert(it, ImGuiStoragePair(key, val));
1945 return;
1946 }
1947 it->val_p = val;
1948 }
1949
SetAllInt(int v)1950 void ImGuiStorage::SetAllInt(int v)
1951 {
1952 for (int i = 0; i < Data.Size; i++)
1953 Data[i].val_i = v;
1954 }
1955
1956 //-----------------------------------------------------------------------------
1957 // [SECTION] ImGuiTextFilter
1958 //-----------------------------------------------------------------------------
1959
1960 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1961 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1962 {
1963 if (default_filter)
1964 {
1965 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1966 Build();
1967 }
1968 else
1969 {
1970 InputBuf[0] = 0;
1971 CountGrep = 0;
1972 }
1973 }
1974
Draw(const char * label,float width)1975 bool ImGuiTextFilter::Draw(const char* label, float width)
1976 {
1977 if (width != 0.0f)
1978 ImGui::SetNextItemWidth(width);
1979 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1980 if (value_changed)
1981 Build();
1982 return value_changed;
1983 }
1984
split(char separator,ImVector<ImGuiTextRange> * out) const1985 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1986 {
1987 out->resize(0);
1988 const char* wb = b;
1989 const char* we = wb;
1990 while (we < e)
1991 {
1992 if (*we == separator)
1993 {
1994 out->push_back(ImGuiTextRange(wb, we));
1995 wb = we + 1;
1996 }
1997 we++;
1998 }
1999 if (wb != we)
2000 out->push_back(ImGuiTextRange(wb, we));
2001 }
2002
Build()2003 void ImGuiTextFilter::Build()
2004 {
2005 Filters.resize(0);
2006 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));
2007 input_range.split(',', &Filters);
2008
2009 CountGrep = 0;
2010 for (int i = 0; i != Filters.Size; i++)
2011 {
2012 ImGuiTextRange& f = Filters[i];
2013 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2014 f.b++;
2015 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2016 f.e--;
2017 if (f.empty())
2018 continue;
2019 if (Filters[i].b[0] != '-')
2020 CountGrep += 1;
2021 }
2022 }
2023
PassFilter(const char * text,const char * text_end) const2024 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2025 {
2026 if (Filters.empty())
2027 return true;
2028
2029 if (text == NULL)
2030 text = "";
2031
2032 for (int i = 0; i != Filters.Size; i++)
2033 {
2034 const ImGuiTextRange& f = Filters[i];
2035 if (f.empty())
2036 continue;
2037 if (f.b[0] == '-')
2038 {
2039 // Subtract
2040 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2041 return false;
2042 }
2043 else
2044 {
2045 // Grep
2046 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2047 return true;
2048 }
2049 }
2050
2051 // Implicit * grep
2052 if (CountGrep == 0)
2053 return true;
2054
2055 return false;
2056 }
2057
2058 //-----------------------------------------------------------------------------
2059 // [SECTION] ImGuiTextBuffer
2060 //-----------------------------------------------------------------------------
2061
2062 // On some platform vsnprintf() takes va_list by reference and modifies it.
2063 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2064 #ifndef va_copy
2065 #if defined(__GNUC__) || defined(__clang__)
2066 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2067 #else
2068 #define va_copy(dest, src) (dest = src)
2069 #endif
2070 #endif
2071
2072 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2073
append(const char * str,const char * str_end)2074 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2075 {
2076 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2077
2078 // Add zero-terminator the first time
2079 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2080 const int needed_sz = write_off + len;
2081 if (write_off + len >= Buf.Capacity)
2082 {
2083 int new_capacity = Buf.Capacity * 2;
2084 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2085 }
2086
2087 Buf.resize(needed_sz);
2088 memcpy(&Buf[write_off - 1], str, (size_t)len);
2089 Buf[write_off - 1 + len] = 0;
2090 }
2091
appendf(const char * fmt,...)2092 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2093 {
2094 va_list args;
2095 va_start(args, fmt);
2096 appendfv(fmt, args);
2097 va_end(args);
2098 }
2099
2100 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2101 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2102 {
2103 va_list args_copy;
2104 va_copy(args_copy, args);
2105
2106 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2107 if (len <= 0)
2108 {
2109 va_end(args_copy);
2110 return;
2111 }
2112
2113 // Add zero-terminator the first time
2114 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2115 const int needed_sz = write_off + len;
2116 if (write_off + len >= Buf.Capacity)
2117 {
2118 int new_capacity = Buf.Capacity * 2;
2119 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2120 }
2121
2122 Buf.resize(needed_sz);
2123 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2124 va_end(args_copy);
2125 }
2126
2127 //-----------------------------------------------------------------------------
2128 // [SECTION] ImGuiListClipper
2129 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2130 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2131 //-----------------------------------------------------------------------------
2132
2133 // Helper to calculate coarse clipping of large list of evenly sized items.
2134 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2135 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX
CalcListClipping(int items_count,float items_height,int * out_items_display_start,int * out_items_display_end)2136 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2137 {
2138 ImGuiContext& g = *GImGui;
2139 ImGuiWindow* window = g.CurrentWindow;
2140 if (g.LogEnabled)
2141 {
2142 // If logging is active, do not perform any clipping
2143 *out_items_display_start = 0;
2144 *out_items_display_end = items_count;
2145 return;
2146 }
2147 if (window->SkipItems)
2148 {
2149 *out_items_display_start = *out_items_display_end = 0;
2150 return;
2151 }
2152
2153 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2154 ImRect unclipped_rect = window->ClipRect;
2155 if (g.NavMoveRequest)
2156 unclipped_rect.Add(g.NavScoringRect);
2157 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
2158 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max));
2159
2160 const ImVec2 pos = window->DC.CursorPos;
2161 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2162 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2163
2164 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2165 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2166 start--;
2167 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2168 end++;
2169
2170 start = ImClamp(start, 0, items_count);
2171 end = ImClamp(end + 1, start, items_count);
2172 *out_items_display_start = start;
2173 *out_items_display_end = end;
2174 }
2175
SetCursorPosYAndSetupForPrevLine(float pos_y,float line_height)2176 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
2177 {
2178 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2179 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2180 // The clipper should probably have a 4th step to display the last item in a regular manner.
2181 ImGuiContext& g = *GImGui;
2182 ImGuiWindow* window = g.CurrentWindow;
2183 window->DC.CursorPos.y = pos_y;
2184 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2185 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.
2186 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.
2187 if (ImGuiColumns* columns = window->DC.CurrentColumns)
2188 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2189 }
2190
2191 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2192 // Use case B: Begin() called from constructor with items_height>0
2193 // FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
Begin(int count,float items_height)2194 void ImGuiListClipper::Begin(int count, float items_height)
2195 {
2196 ImGuiContext& g = *GImGui;
2197 ImGuiWindow* window = g.CurrentWindow;
2198
2199 StartPosY = window->DC.CursorPos.y;
2200 ItemsHeight = items_height;
2201 ItemsCount = count;
2202 StepNo = 0;
2203 DisplayEnd = DisplayStart = -1;
2204 if (ItemsHeight > 0.0f)
2205 {
2206 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2207 if (DisplayStart > 0)
2208 SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2209 StepNo = 2;
2210 }
2211 }
2212
End()2213 void ImGuiListClipper::End()
2214 {
2215 if (ItemsCount < 0)
2216 return;
2217 // 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.
2218 if (ItemsCount < INT_MAX)
2219 SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2220 ItemsCount = -1;
2221 StepNo = 3;
2222 }
2223
Step()2224 bool ImGuiListClipper::Step()
2225 {
2226 ImGuiContext& g = *GImGui;
2227 ImGuiWindow* window = g.CurrentWindow;
2228
2229 if (ItemsCount == 0 || window->SkipItems)
2230 {
2231 ItemsCount = -1;
2232 return false;
2233 }
2234 if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
2235 {
2236 DisplayStart = 0;
2237 DisplayEnd = 1;
2238 StartPosY = window->DC.CursorPos.y;
2239 StepNo = 1;
2240 return true;
2241 }
2242 if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
2243 {
2244 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2245 float items_height = window->DC.CursorPos.y - StartPosY;
2246 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2247 Begin(ItemsCount - 1, items_height);
2248 DisplayStart++;
2249 DisplayEnd++;
2250 StepNo = 3;
2251 return true;
2252 }
2253 if (StepNo == 2) // Step 2: empty step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2254 {
2255 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2256 StepNo = 3;
2257 return true;
2258 }
2259 if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop.
2260 End();
2261 return false;
2262 }
2263
2264 //-----------------------------------------------------------------------------
2265 // [SECTION] STYLING
2266 //-----------------------------------------------------------------------------
2267
GetStyle()2268 ImGuiStyle& ImGui::GetStyle()
2269 {
2270 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
2271 return GImGui->Style;
2272 }
2273
GetColorU32(ImGuiCol idx,float alpha_mul)2274 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2275 {
2276 ImGuiStyle& style = GImGui->Style;
2277 ImVec4 c = style.Colors[idx];
2278 c.w *= style.Alpha * alpha_mul;
2279 return ColorConvertFloat4ToU32(c);
2280 }
2281
GetColorU32(const ImVec4 & col)2282 ImU32 ImGui::GetColorU32(const ImVec4& col)
2283 {
2284 ImGuiStyle& style = GImGui->Style;
2285 ImVec4 c = col;
2286 c.w *= style.Alpha;
2287 return ColorConvertFloat4ToU32(c);
2288 }
2289
GetStyleColorVec4(ImGuiCol idx)2290 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2291 {
2292 ImGuiStyle& style = GImGui->Style;
2293 return style.Colors[idx];
2294 }
2295
GetColorU32(ImU32 col)2296 ImU32 ImGui::GetColorU32(ImU32 col)
2297 {
2298 ImGuiStyle& style = GImGui->Style;
2299 if (style.Alpha >= 1.0f)
2300 return col;
2301 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2302 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2303 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2304 }
2305
2306 // FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
PushStyleColor(ImGuiCol idx,ImU32 col)2307 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
2308 {
2309 ImGuiContext& g = *GImGui;
2310 ImGuiColorMod backup;
2311 backup.Col = idx;
2312 backup.BackupValue = g.Style.Colors[idx];
2313 g.ColorModifiers.push_back(backup);
2314 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
2315 }
2316
PushStyleColor(ImGuiCol idx,const ImVec4 & col)2317 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
2318 {
2319 ImGuiContext& g = *GImGui;
2320 ImGuiColorMod backup;
2321 backup.Col = idx;
2322 backup.BackupValue = g.Style.Colors[idx];
2323 g.ColorModifiers.push_back(backup);
2324 g.Style.Colors[idx] = col;
2325 }
2326
PopStyleColor(int count)2327 void ImGui::PopStyleColor(int count)
2328 {
2329 ImGuiContext& g = *GImGui;
2330 while (count > 0)
2331 {
2332 ImGuiColorMod& backup = g.ColorModifiers.back();
2333 g.Style.Colors[backup.Col] = backup.BackupValue;
2334 g.ColorModifiers.pop_back();
2335 count--;
2336 }
2337 }
2338
2339 struct ImGuiStyleVarInfo
2340 {
2341 ImGuiDataType Type;
2342 ImU32 Count;
2343 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo2344 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
2345 };
2346
2347 static const ImGuiStyleVarInfo GStyleVarInfo[] =
2348 {
2349 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
2350 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
2351 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
2352 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
2353 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
2354 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
2355 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
2356 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
2357 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
2358 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
2359 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
2360 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
2361 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
2362 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
2363 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
2364 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
2365 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
2366 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
2367 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
2368 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
2369 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
2370 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
2371 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
2372 };
2373
GetStyleVarInfo(ImGuiStyleVar idx)2374 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
2375 {
2376 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
2377 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
2378 return &GStyleVarInfo[idx];
2379 }
2380
PushStyleVar(ImGuiStyleVar idx,float val)2381 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
2382 {
2383 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2384 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
2385 {
2386 ImGuiContext& g = *GImGui;
2387 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
2388 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
2389 *pvar = val;
2390 return;
2391 }
2392 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
2393 }
2394
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)2395 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
2396 {
2397 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
2398 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
2399 {
2400 ImGuiContext& g = *GImGui;
2401 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
2402 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
2403 *pvar = val;
2404 return;
2405 }
2406 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
2407 }
2408
PopStyleVar(int count)2409 void ImGui::PopStyleVar(int count)
2410 {
2411 ImGuiContext& g = *GImGui;
2412 while (count > 0)
2413 {
2414 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
2415 ImGuiStyleMod& backup = g.StyleModifiers.back();
2416 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
2417 void* data = info->GetVarPtr(&g.Style);
2418 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
2419 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
2420 g.StyleModifiers.pop_back();
2421 count--;
2422 }
2423 }
2424
GetStyleColorName(ImGuiCol idx)2425 const char* ImGui::GetStyleColorName(ImGuiCol idx)
2426 {
2427 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
2428 switch (idx)
2429 {
2430 case ImGuiCol_Text: return "Text";
2431 case ImGuiCol_TextDisabled: return "TextDisabled";
2432 case ImGuiCol_WindowBg: return "WindowBg";
2433 case ImGuiCol_ChildBg: return "ChildBg";
2434 case ImGuiCol_PopupBg: return "PopupBg";
2435 case ImGuiCol_Border: return "Border";
2436 case ImGuiCol_BorderShadow: return "BorderShadow";
2437 case ImGuiCol_FrameBg: return "FrameBg";
2438 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
2439 case ImGuiCol_FrameBgActive: return "FrameBgActive";
2440 case ImGuiCol_TitleBg: return "TitleBg";
2441 case ImGuiCol_TitleBgActive: return "TitleBgActive";
2442 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
2443 case ImGuiCol_MenuBarBg: return "MenuBarBg";
2444 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
2445 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
2446 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
2447 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
2448 case ImGuiCol_CheckMark: return "CheckMark";
2449 case ImGuiCol_SliderGrab: return "SliderGrab";
2450 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
2451 case ImGuiCol_Button: return "Button";
2452 case ImGuiCol_ButtonHovered: return "ButtonHovered";
2453 case ImGuiCol_ButtonActive: return "ButtonActive";
2454 case ImGuiCol_Header: return "Header";
2455 case ImGuiCol_HeaderHovered: return "HeaderHovered";
2456 case ImGuiCol_HeaderActive: return "HeaderActive";
2457 case ImGuiCol_Separator: return "Separator";
2458 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
2459 case ImGuiCol_SeparatorActive: return "SeparatorActive";
2460 case ImGuiCol_ResizeGrip: return "ResizeGrip";
2461 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
2462 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
2463 case ImGuiCol_Tab: return "Tab";
2464 case ImGuiCol_TabHovered: return "TabHovered";
2465 case ImGuiCol_TabActive: return "TabActive";
2466 case ImGuiCol_TabUnfocused: return "TabUnfocused";
2467 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
2468 case ImGuiCol_PlotLines: return "PlotLines";
2469 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
2470 case ImGuiCol_PlotHistogram: return "PlotHistogram";
2471 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
2472 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
2473 case ImGuiCol_DragDropTarget: return "DragDropTarget";
2474 case ImGuiCol_NavHighlight: return "NavHighlight";
2475 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
2476 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
2477 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
2478 }
2479 IM_ASSERT(0);
2480 return "Unknown";
2481 }
2482
2483
2484 //-----------------------------------------------------------------------------
2485 // [SECTION] RENDER HELPERS
2486 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
2487 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
2488 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
2489 //-----------------------------------------------------------------------------
2490
FindRenderedTextEnd(const char * text,const char * text_end)2491 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2492 {
2493 const char* text_display_end = text;
2494 if (!text_end)
2495 text_end = (const char*)-1;
2496
2497 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2498 text_display_end++;
2499 return text_display_end;
2500 }
2501
2502 // Internal ImGui functions to render text
2503 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2504 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2505 {
2506 ImGuiContext& g = *GImGui;
2507 ImGuiWindow* window = g.CurrentWindow;
2508
2509 // Hide anything after a '##' string
2510 const char* text_display_end;
2511 if (hide_text_after_hash)
2512 {
2513 text_display_end = FindRenderedTextEnd(text, text_end);
2514 }
2515 else
2516 {
2517 if (!text_end)
2518 text_end = text + strlen(text); // FIXME-OPT
2519 text_display_end = text_end;
2520 }
2521
2522 if (text != text_display_end)
2523 {
2524 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2525 if (g.LogEnabled)
2526 LogRenderedText(&pos, text, text_display_end);
2527 }
2528 }
2529
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2530 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2531 {
2532 ImGuiContext& g = *GImGui;
2533 ImGuiWindow* window = g.CurrentWindow;
2534
2535 if (!text_end)
2536 text_end = text + strlen(text); // FIXME-OPT
2537
2538 if (text != text_end)
2539 {
2540 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2541 if (g.LogEnabled)
2542 LogRenderedText(&pos, text, text_end);
2543 }
2544 }
2545
2546 // Default clip_rect uses (pos_min,pos_max)
2547 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
RenderTextClippedEx(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,const char * text,const char * text_display_end,const ImVec2 * text_size_if_known,const ImVec2 & align,const ImRect * clip_rect)2548 void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2549 {
2550 // Perform CPU side clipping for single clipped element to avoid using scissor state
2551 ImVec2 pos = pos_min;
2552 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2553
2554 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2555 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2556 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2557 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2558 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2559
2560 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2561 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2562 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2563
2564 // Render
2565 if (need_clipping)
2566 {
2567 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2568 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2569 }
2570 else
2571 {
2572 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2573 }
2574 }
2575
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)2576 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
2577 {
2578 // Hide anything after a '##' string
2579 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2580 const int text_len = (int)(text_display_end - text);
2581 if (text_len == 0)
2582 return;
2583
2584 ImGuiContext& g = *GImGui;
2585 ImGuiWindow* window = g.CurrentWindow;
2586 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2587 if (g.LogEnabled)
2588 LogRenderedText(&pos_min, text, text_display_end);
2589 }
2590
2591
2592 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2593 // 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.
2594 // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
RenderTextEllipsis(ImDrawList * draw_list,const ImVec2 & pos_min,const ImVec2 & pos_max,float clip_max_x,float ellipsis_max_x,const char * text,const char * text_end_full,const ImVec2 * text_size_if_known)2595 void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
2596 {
2597 ImGuiContext& g = *GImGui;
2598 if (text_end_full == NULL)
2599 text_end_full = FindRenderedTextEnd(text);
2600 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2601
2602 //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));
2603 //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));
2604 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2605 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2606 if (text_size.x > pos_max.x - pos_min.x)
2607 {
2608 // Hello wo...
2609 // | | |
2610 // min max ellipsis_max
2611 // <-> this is generally some padding value
2612
2613 const ImFont* font = draw_list->_Data->Font;
2614 const float font_size = draw_list->_Data->FontSize;
2615 const char* text_end_ellipsis = NULL;
2616
2617 ImWchar ellipsis_char = font->EllipsisChar;
2618 int ellipsis_char_count = 1;
2619 if (ellipsis_char == (ImWchar)-1)
2620 {
2621 ellipsis_char = (ImWchar)'.';
2622 ellipsis_char_count = 3;
2623 }
2624 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2625
2626 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2627 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2628
2629 if (ellipsis_char_count > 1)
2630 {
2631 // Full ellipsis size without free spacing after it.
2632 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2633 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2634 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2635 }
2636
2637 // We can now claim the space between pos_max.x and ellipsis_max.x
2638 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2639 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2640 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2641 {
2642 // Always display at least 1 character if there's no room for character + ellipsis
2643 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2644 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2645 }
2646 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2647 {
2648 // 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)
2649 text_end_ellipsis--;
2650 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
2651 }
2652
2653 // Render text, render ellipsis
2654 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2655 float ellipsis_x = pos_min.x + text_size_clipped_x;
2656 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2657 for (int i = 0; i < ellipsis_char_count; i++)
2658 {
2659 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2660 ellipsis_x += ellipsis_glyph_width;
2661 }
2662 }
2663 else
2664 {
2665 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2666 }
2667
2668 if (g.LogEnabled)
2669 LogRenderedText(&pos_min, text, text_end_full);
2670 }
2671
2672 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2673 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2674 {
2675 ImGuiContext& g = *GImGui;
2676 ImGuiWindow* window = g.CurrentWindow;
2677 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2678 const float border_size = g.Style.FrameBorderSize;
2679 if (border && border_size > 0.0f)
2680 {
2681 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2682 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2683 }
2684 }
2685
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2686 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2687 {
2688 ImGuiContext& g = *GImGui;
2689 ImGuiWindow* window = g.CurrentWindow;
2690 const float border_size = g.Style.FrameBorderSize;
2691 if (border_size > 0.0f)
2692 {
2693 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2694 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2695 }
2696 }
2697
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2698 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2699 {
2700 ImGuiContext& g = *GImGui;
2701 if (id != g.NavId)
2702 return;
2703 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2704 return;
2705 ImGuiWindow* window = g.CurrentWindow;
2706 if (window->DC.NavHideHighlightOneFrame)
2707 return;
2708
2709 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2710 ImRect display_rect = bb;
2711 display_rect.ClipWith(window->ClipRect);
2712 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2713 {
2714 const float THICKNESS = 2.0f;
2715 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2716 display_rect.Expand(ImVec2(DISTANCE, DISTANCE));
2717 bool fully_visible = window->ClipRect.Contains(display_rect);
2718 if (!fully_visible)
2719 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2720 window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS);
2721 if (!fully_visible)
2722 window->DrawList->PopClipRect();
2723 }
2724 if (flags & ImGuiNavHighlightFlags_TypeThin)
2725 {
2726 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2727 }
2728 }
2729
2730 //-----------------------------------------------------------------------------
2731 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2732 //-----------------------------------------------------------------------------
2733
2734 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2735 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2736 : DrawListInst(&context->DrawListSharedData)
2737 {
2738 Name = ImStrdup(name);
2739 ID = ImHashStr(name);
2740 IDStack.push_back(ID);
2741 Flags = ImGuiWindowFlags_None;
2742 Pos = ImVec2(0.0f, 0.0f);
2743 Size = SizeFull = ImVec2(0.0f, 0.0f);
2744 ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2745 WindowPadding = ImVec2(0.0f, 0.0f);
2746 WindowRounding = 0.0f;
2747 WindowBorderSize = 0.0f;
2748 NameBufLen = (int)strlen(name) + 1;
2749 MoveId = GetID("#MOVE");
2750 ChildId = 0;
2751 Scroll = ImVec2(0.0f, 0.0f);
2752 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2753 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2754 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2755 ScrollbarX = ScrollbarY = false;
2756 Active = WasActive = false;
2757 WriteAccessed = false;
2758 Collapsed = false;
2759 WantCollapseToggle = false;
2760 SkipItems = false;
2761 Appearing = false;
2762 Hidden = false;
2763 IsFallbackWindow = false;
2764 HasCloseButton = false;
2765 ResizeBorderHeld = -1;
2766 BeginCount = 0;
2767 BeginOrderWithinParent = -1;
2768 BeginOrderWithinContext = -1;
2769 PopupId = 0;
2770 AutoFitFramesX = AutoFitFramesY = -1;
2771 AutoFitChildAxises = 0x00;
2772 AutoFitOnlyGrows = false;
2773 AutoPosLastDirection = ImGuiDir_None;
2774 HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2775 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2776 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2777
2778 InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used.
2779
2780 LastFrameActive = -1;
2781 LastTimeActive = -1.0f;
2782 ItemWidthDefault = 0.0f;
2783 FontWindowScale = 1.0f;
2784 SettingsOffset = -1;
2785
2786 DrawList = &DrawListInst;
2787 DrawList->_OwnerName = Name;
2788 ParentWindow = NULL;
2789 RootWindow = NULL;
2790 RootWindowForTitleBarHighlight = NULL;
2791 RootWindowForNav = NULL;
2792
2793 NavLastIds[0] = NavLastIds[1] = 0;
2794 NavRectRel[0] = NavRectRel[1] = ImRect();
2795 NavLastChildNavWindow = NULL;
2796
2797 MemoryCompacted = false;
2798 MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2799 }
2800
~ImGuiWindow()2801 ImGuiWindow::~ImGuiWindow()
2802 {
2803 IM_ASSERT(DrawList == &DrawListInst);
2804 IM_DELETE(Name);
2805 for (int i = 0; i != ColumnsStorage.Size; i++)
2806 ColumnsStorage[i].~ImGuiColumns();
2807 }
2808
GetID(const char * str,const char * str_end)2809 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2810 {
2811 ImGuiID seed = IDStack.back();
2812 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2813 ImGui::KeepAliveID(id);
2814 #ifdef IMGUI_ENABLE_TEST_ENGINE
2815 ImGuiContext& g = *GImGui;
2816 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2817 #endif
2818 return id;
2819 }
2820
GetID(const void * ptr)2821 ImGuiID ImGuiWindow::GetID(const void* ptr)
2822 {
2823 ImGuiID seed = IDStack.back();
2824 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2825 ImGui::KeepAliveID(id);
2826 #ifdef IMGUI_ENABLE_TEST_ENGINE
2827 ImGuiContext& g = *GImGui;
2828 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2829 #endif
2830 return id;
2831 }
2832
GetID(int n)2833 ImGuiID ImGuiWindow::GetID(int n)
2834 {
2835 ImGuiID seed = IDStack.back();
2836 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2837 ImGui::KeepAliveID(id);
2838 #ifdef IMGUI_ENABLE_TEST_ENGINE
2839 ImGuiContext& g = *GImGui;
2840 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2841 #endif
2842 return id;
2843 }
2844
GetIDNoKeepAlive(const char * str,const char * str_end)2845 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2846 {
2847 ImGuiID seed = IDStack.back();
2848 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2849 #ifdef IMGUI_ENABLE_TEST_ENGINE
2850 ImGuiContext& g = *GImGui;
2851 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end);
2852 #endif
2853 return id;
2854 }
2855
GetIDNoKeepAlive(const void * ptr)2856 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2857 {
2858 ImGuiID seed = IDStack.back();
2859 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2860 #ifdef IMGUI_ENABLE_TEST_ENGINE
2861 ImGuiContext& g = *GImGui;
2862 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr);
2863 #endif
2864 return id;
2865 }
2866
GetIDNoKeepAlive(int n)2867 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2868 {
2869 ImGuiID seed = IDStack.back();
2870 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2871 #ifdef IMGUI_ENABLE_TEST_ENGINE
2872 ImGuiContext& g = *GImGui;
2873 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n);
2874 #endif
2875 return id;
2876 }
2877
2878 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2879 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2880 {
2881 ImGuiID seed = IDStack.back();
2882 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) };
2883 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2884 ImGui::KeepAliveID(id);
2885 return id;
2886 }
2887
SetCurrentWindow(ImGuiWindow * window)2888 static void SetCurrentWindow(ImGuiWindow* window)
2889 {
2890 ImGuiContext& g = *GImGui;
2891 g.CurrentWindow = window;
2892 if (window)
2893 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2894 }
2895
2896 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2897 // This is currently unused by the library, but you may call this yourself for easy GC.
2898 // Not freed:
2899 // - ImGuiWindow, ImGuiWindowSettings, Name
2900 // - StateStorage, ColumnsStorage (may hold useful data)
2901 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2902 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2903 {
2904 window->MemoryCompacted = true;
2905 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2906 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2907 window->IDStack.clear();
2908 window->DrawList->_ClearFreeMemory();
2909 window->DC.ChildWindows.clear();
2910 window->DC.ItemFlagsStack.clear();
2911 window->DC.ItemWidthStack.clear();
2912 window->DC.TextWrapPosStack.clear();
2913 window->DC.GroupStack.clear();
2914 }
2915
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2916 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2917 {
2918 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2919 // The other buffers tends to amortize much faster.
2920 window->MemoryCompacted = false;
2921 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2922 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2923 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2924 }
2925
SetActiveID(ImGuiID id,ImGuiWindow * window)2926 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2927 {
2928 ImGuiContext& g = *GImGui;
2929 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2930 if (g.ActiveIdIsJustActivated)
2931 {
2932 g.ActiveIdTimer = 0.0f;
2933 g.ActiveIdHasBeenPressedBefore = false;
2934 g.ActiveIdHasBeenEditedBefore = false;
2935 if (id != 0)
2936 {
2937 g.LastActiveId = id;
2938 g.LastActiveIdTimer = 0.0f;
2939 }
2940 }
2941 g.ActiveId = id;
2942 g.ActiveIdAllowOverlap = false;
2943 g.ActiveIdNoClearOnFocusLoss = false;
2944 g.ActiveIdWindow = window;
2945 g.ActiveIdHasBeenEditedThisFrame = false;
2946 if (id)
2947 {
2948 g.ActiveIdIsAlive = id;
2949 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2950 }
2951
2952 // Clear declaration of inputs claimed by the widget
2953 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2954 g.ActiveIdUsingNavDirMask = 0x00;
2955 g.ActiveIdUsingNavInputMask = 0x00;
2956 g.ActiveIdUsingKeyInputMask = 0x00;
2957 }
2958
ClearActiveID()2959 void ImGui::ClearActiveID()
2960 {
2961 SetActiveID(0, NULL); // g.ActiveId = 0;
2962 }
2963
SetHoveredID(ImGuiID id)2964 void ImGui::SetHoveredID(ImGuiID id)
2965 {
2966 ImGuiContext& g = *GImGui;
2967 g.HoveredId = id;
2968 g.HoveredIdAllowOverlap = false;
2969 if (id != 0 && g.HoveredIdPreviousFrame != id)
2970 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2971 }
2972
GetHoveredID()2973 ImGuiID ImGui::GetHoveredID()
2974 {
2975 ImGuiContext& g = *GImGui;
2976 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2977 }
2978
KeepAliveID(ImGuiID id)2979 void ImGui::KeepAliveID(ImGuiID id)
2980 {
2981 ImGuiContext& g = *GImGui;
2982 if (g.ActiveId == id)
2983 g.ActiveIdIsAlive = id;
2984 if (g.ActiveIdPreviousFrame == id)
2985 g.ActiveIdPreviousFrameIsAlive = true;
2986 }
2987
MarkItemEdited(ImGuiID id)2988 void ImGui::MarkItemEdited(ImGuiID id)
2989 {
2990 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2991 // 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.
2992 ImGuiContext& g = *GImGui;
2993 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2994 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2995 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2996 g.ActiveIdHasBeenEditedThisFrame = true;
2997 g.ActiveIdHasBeenEditedBefore = true;
2998 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2999 }
3000
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)3001 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
3002 {
3003 // An active popup disable hovering on other windows (apart from its own children)
3004 // FIXME-OPT: This could be cached/stored within the window.
3005 ImGuiContext& g = *GImGui;
3006 if (g.NavWindow)
3007 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
3008 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
3009 {
3010 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
3011 // NB: The order of those two tests is important because Modal windows are also Popups.
3012 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
3013 return false;
3014 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
3015 return false;
3016 }
3017 return true;
3018 }
3019
3020 // This is roughly matching the behavior of internal-facing ItemHoverable()
3021 // - 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()
3022 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)3023 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
3024 {
3025 ImGuiContext& g = *GImGui;
3026 ImGuiWindow* window = g.CurrentWindow;
3027 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
3028 return IsItemFocused();
3029
3030 // Test for bounding box overlap, as updated as ItemAdd()
3031 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
3032 return false;
3033 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
3034
3035 // Test if we are hovering the right window (our window could be behind another window)
3036 // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself.
3037 // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
3038 //if (g.HoveredWindow != window)
3039 // return false;
3040 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
3041 return false;
3042
3043 // Test if another item is active (e.g. being dragged)
3044 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
3045 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
3046 return false;
3047
3048 // Test if interactions on this window are blocked by an active popup or modal.
3049 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
3050 if (!IsWindowContentHoverable(window, flags))
3051 return false;
3052
3053 // Test if the item is disabled
3054 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
3055 return false;
3056
3057 // Special handling for calling after Begin() which represent the title bar or tab.
3058 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
3059 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
3060 return false;
3061 return true;
3062 }
3063
3064 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)3065 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
3066 {
3067 ImGuiContext& g = *GImGui;
3068 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3069 return false;
3070
3071 ImGuiWindow* window = g.CurrentWindow;
3072 if (g.HoveredWindow != window)
3073 return false;
3074 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3075 return false;
3076 if (!IsMouseHoveringRect(bb.Min, bb.Max))
3077 return false;
3078 if (g.NavDisableMouseHover)
3079 return false;
3080 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled))
3081 {
3082 g.HoveredIdDisabled = true;
3083 return false;
3084 }
3085
3086 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
3087 // hover test in widgets code. We could also decide to split this function is two.
3088 if (id != 0)
3089 {
3090 SetHoveredID(id);
3091
3092 // [DEBUG] Item Picker tool!
3093 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3094 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3095 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3096 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3097 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3098 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3099 if (g.DebugItemPickerBreakId == id)
3100 IM_DEBUG_BREAK();
3101 }
3102
3103 return true;
3104 }
3105
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3106 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3107 {
3108 ImGuiContext& g = *GImGui;
3109 ImGuiWindow* window = g.CurrentWindow;
3110 if (!bb.Overlaps(window->ClipRect))
3111 if (id == 0 || (id != g.ActiveId && id != g.NavId))
3112 if (clip_even_when_logged || !g.LogEnabled)
3113 return true;
3114 return false;
3115 }
3116
3117 // This is also inlined in ItemAdd()
3118 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect!
SetLastItemData(ImGuiWindow * window,ImGuiID item_id,ImGuiItemStatusFlags item_flags,const ImRect & item_rect)3119 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)
3120 {
3121 window->DC.LastItemId = item_id;
3122 window->DC.LastItemStatusFlags = item_flags;
3123 window->DC.LastItemRect = item_rect;
3124 }
3125
3126 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3127 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3128 {
3129 ImGuiContext& g = *GImGui;
3130
3131 // Increment counters
3132 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3133 window->DC.FocusCounterRegular++;
3134 if (is_tab_stop)
3135 window->DC.FocusCounterTabStop++;
3136
3137 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3138 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3139 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3140 {
3141 g.FocusRequestNextWindow = window;
3142 g.FocusRequestNextCounterTabStop = 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.
3143 }
3144
3145 // Handle focus requests
3146 if (g.FocusRequestCurrWindow == window)
3147 {
3148 if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3149 return true;
3150 if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3151 {
3152 g.NavJustTabbedId = id;
3153 return true;
3154 }
3155
3156 // If another item is about to be focused, we clear our own active id
3157 if (g.ActiveId == id)
3158 ClearActiveID();
3159 }
3160
3161 return false;
3162 }
3163
FocusableItemUnregister(ImGuiWindow * window)3164 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3165 {
3166 window->DC.FocusCounterRegular--;
3167 window->DC.FocusCounterTabStop--;
3168 }
3169
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3170 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3171 {
3172 if (wrap_pos_x < 0.0f)
3173 return 0.0f;
3174
3175 ImGuiContext& g = *GImGui;
3176 ImGuiWindow* window = g.CurrentWindow;
3177 if (wrap_pos_x == 0.0f)
3178 {
3179 // We could decide to setup a default wrapping max point for auto-resizing windows,
3180 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
3181 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
3182 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
3183 //else
3184 wrap_pos_x = window->WorkRect.Max.x;
3185 }
3186 else if (wrap_pos_x > 0.0f)
3187 {
3188 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3189 }
3190
3191 return ImMax(wrap_pos_x - pos.x, 1.0f);
3192 }
3193
3194 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3195 void* ImGui::MemAlloc(size_t size)
3196 {
3197 if (ImGuiContext* ctx = GImGui)
3198 ctx->IO.MetricsActiveAllocations++;
3199 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3200 }
3201
3202 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3203 void ImGui::MemFree(void* ptr)
3204 {
3205 if (ptr)
3206 if (ImGuiContext* ctx = GImGui)
3207 ctx->IO.MetricsActiveAllocations--;
3208 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3209 }
3210
GetClipboardText()3211 const char* ImGui::GetClipboardText()
3212 {
3213 ImGuiContext& g = *GImGui;
3214 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3215 }
3216
SetClipboardText(const char * text)3217 void ImGui::SetClipboardText(const char* text)
3218 {
3219 ImGuiContext& g = *GImGui;
3220 if (g.IO.SetClipboardTextFn)
3221 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3222 }
3223
GetVersion()3224 const char* ImGui::GetVersion()
3225 {
3226 return IMGUI_VERSION;
3227 }
3228
3229 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3230 // Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
GetCurrentContext()3231 ImGuiContext* ImGui::GetCurrentContext()
3232 {
3233 return GImGui;
3234 }
3235
SetCurrentContext(ImGuiContext * ctx)3236 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3237 {
3238 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3239 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3240 #else
3241 GImGui = ctx;
3242 #endif
3243 }
3244
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3245 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3246 {
3247 GImAllocatorAllocFunc = alloc_func;
3248 GImAllocatorFreeFunc = free_func;
3249 GImAllocatorUserData = user_data;
3250 }
3251
CreateContext(ImFontAtlas * shared_font_atlas)3252 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3253 {
3254 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3255 if (GImGui == NULL)
3256 SetCurrentContext(ctx);
3257 Initialize(ctx);
3258 return ctx;
3259 }
3260
DestroyContext(ImGuiContext * ctx)3261 void ImGui::DestroyContext(ImGuiContext* ctx)
3262 {
3263 if (ctx == NULL)
3264 ctx = GImGui;
3265 Shutdown(ctx);
3266 if (GImGui == ctx)
3267 SetCurrentContext(NULL);
3268 IM_DELETE(ctx);
3269 }
3270
GetIO()3271 ImGuiIO& ImGui::GetIO()
3272 {
3273 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3274 return GImGui->IO;
3275 }
3276
3277 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3278 ImDrawData* ImGui::GetDrawData()
3279 {
3280 ImGuiContext& g = *GImGui;
3281 return g.DrawData.Valid ? &g.DrawData : NULL;
3282 }
3283
GetTime()3284 double ImGui::GetTime()
3285 {
3286 return GImGui->Time;
3287 }
3288
GetFrameCount()3289 int ImGui::GetFrameCount()
3290 {
3291 return GImGui->FrameCount;
3292 }
3293
GetBackgroundDrawList()3294 ImDrawList* ImGui::GetBackgroundDrawList()
3295 {
3296 return &GImGui->BackgroundDrawList;
3297 }
3298
GetForegroundDrawList()3299 ImDrawList* ImGui::GetForegroundDrawList()
3300 {
3301 return &GImGui->ForegroundDrawList;
3302 }
3303
GetDrawListSharedData()3304 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3305 {
3306 return &GImGui->DrawListSharedData;
3307 }
3308
StartMouseMovingWindow(ImGuiWindow * window)3309 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3310 {
3311 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3312 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3313 // This is because we want ActiveId to be set even when the window is not permitted to move.
3314 ImGuiContext& g = *GImGui;
3315 FocusWindow(window);
3316 SetActiveID(window->MoveId, window);
3317 g.NavDisableHighlight = true;
3318 g.ActiveIdNoClearOnFocusLoss = true;
3319 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3320
3321 bool can_move_window = true;
3322 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3323 can_move_window = false;
3324 if (can_move_window)
3325 g.MovingWindow = window;
3326 }
3327
3328 // Handle mouse moving window
3329 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
3330 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.
3331 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
3332 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
UpdateMouseMovingWindowNewFrame()3333 void ImGui::UpdateMouseMovingWindowNewFrame()
3334 {
3335 ImGuiContext& g = *GImGui;
3336 if (g.MovingWindow != NULL)
3337 {
3338 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3339 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3340 KeepAliveID(g.ActiveId);
3341 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3342 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3343 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3344 {
3345 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3346 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3347 {
3348 MarkIniSettingsDirty(moving_window);
3349 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3350 }
3351 FocusWindow(g.MovingWindow);
3352 }
3353 else
3354 {
3355 ClearActiveID();
3356 g.MovingWindow = NULL;
3357 }
3358 }
3359 else
3360 {
3361 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3362 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3363 {
3364 KeepAliveID(g.ActiveId);
3365 if (!g.IO.MouseDown[0])
3366 ClearActiveID();
3367 }
3368 }
3369 }
3370
3371 // Initiate moving window when clicking on empty space or title bar.
3372 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3373 void ImGui::UpdateMouseMovingWindowEndFrame()
3374 {
3375 ImGuiContext& g = *GImGui;
3376 if (g.ActiveId != 0 || g.HoveredId != 0)
3377 return;
3378
3379 // Unless we just made a window/popup appear
3380 if (g.NavWindow && g.NavWindow->Appearing)
3381 return;
3382
3383 // Click on empty space to focus window and start moving (after we're done with all our widgets)
3384 if (g.IO.MouseClicked[0])
3385 {
3386 // Handle the edge case of a popup being closed while clicking in its empty space.
3387 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
3388 ImGuiWindow* root_window = g.HoveredRootWindow;
3389 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
3390
3391 if (root_window != NULL && !is_closed_popup)
3392 {
3393 StartMouseMovingWindow(g.HoveredWindow);
3394
3395 // Cancel moving if clicked outside of title bar
3396 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
3397 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3398 g.MovingWindow = NULL;
3399
3400 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)
3401 if (g.HoveredIdDisabled)
3402 g.MovingWindow = NULL;
3403 }
3404 else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3405 {
3406 // Clicking on void disable focus
3407 FocusWindow(NULL);
3408 }
3409 }
3410
3411 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3412 // Instead, focus will be restored to the window under the bottom-most closed popup.
3413 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3414 if (g.IO.MouseClicked[1])
3415 {
3416 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3417 // This is where we can trim the popup stack.
3418 ImGuiWindow* modal = GetTopMostPopupModal();
3419 bool hovered_window_above_modal = false;
3420 if (modal == NULL)
3421 hovered_window_above_modal = true;
3422 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3423 {
3424 ImGuiWindow* window = g.Windows[i];
3425 if (window == modal)
3426 break;
3427 if (window == g.HoveredWindow)
3428 hovered_window_above_modal = true;
3429 }
3430 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3431 }
3432 }
3433
IsWindowActiveAndVisible(ImGuiWindow * window)3434 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3435 {
3436 return (window->Active) && (!window->Hidden);
3437 }
3438
UpdateMouseInputs()3439 static void ImGui::UpdateMouseInputs()
3440 {
3441 ImGuiContext& g = *GImGui;
3442
3443 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3444 if (IsMousePosValid(&g.IO.MousePos))
3445 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3446
3447 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3448 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3449 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3450 else
3451 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3452 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3453 g.NavDisableMouseHover = false;
3454
3455 g.IO.MousePosPrev = g.IO.MousePos;
3456 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3457 {
3458 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3459 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3460 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3461 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;
3462 g.IO.MouseDoubleClicked[i] = false;
3463 if (g.IO.MouseClicked[i])
3464 {
3465 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3466 {
3467 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3468 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3469 g.IO.MouseDoubleClicked[i] = true;
3470 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click
3471 }
3472 else
3473 {
3474 g.IO.MouseClickedTime[i] = g.Time;
3475 }
3476 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3477 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3478 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3479 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3480 }
3481 else if (g.IO.MouseDown[i])
3482 {
3483 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3484 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3485 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3486 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);
3487 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);
3488 }
3489 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3490 g.IO.MouseDownWasDoubleClick[i] = false;
3491 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3492 g.NavDisableMouseHover = false;
3493 }
3494 }
3495
StartLockWheelingWindow(ImGuiWindow * window)3496 static void StartLockWheelingWindow(ImGuiWindow* window)
3497 {
3498 ImGuiContext& g = *GImGui;
3499 if (g.WheelingWindow == window)
3500 return;
3501 g.WheelingWindow = window;
3502 g.WheelingWindowRefMousePos = g.IO.MousePos;
3503 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3504 }
3505
UpdateMouseWheel()3506 void ImGui::UpdateMouseWheel()
3507 {
3508 ImGuiContext& g = *GImGui;
3509
3510 // Reset the locked window if we move the mouse or after the timer elapses
3511 if (g.WheelingWindow != NULL)
3512 {
3513 g.WheelingWindowTimer -= g.IO.DeltaTime;
3514 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3515 g.WheelingWindowTimer = 0.0f;
3516 if (g.WheelingWindowTimer <= 0.0f)
3517 {
3518 g.WheelingWindow = NULL;
3519 g.WheelingWindowTimer = 0.0f;
3520 }
3521 }
3522
3523 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3524 return;
3525
3526 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3527 if (!window || window->Collapsed)
3528 return;
3529
3530 // Zoom / Scale window
3531 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3532 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3533 {
3534 StartLockWheelingWindow(window);
3535 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3536 const float scale = new_font_scale / window->FontWindowScale;
3537 window->FontWindowScale = new_font_scale;
3538 if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3539 {
3540 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3541 SetWindowPos(window, window->Pos + offset, 0);
3542 window->Size = ImFloor(window->Size * scale);
3543 window->SizeFull = ImFloor(window->SizeFull * scale);
3544 }
3545 return;
3546 }
3547
3548 // Mouse wheel scrolling
3549 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3550
3551 // Vertical Mouse Wheel scrolling
3552 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3553 if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3554 {
3555 StartLockWheelingWindow(window);
3556 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3557 window = window->ParentWindow;
3558 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3559 {
3560 float max_step = window->InnerRect.GetHeight() * 0.67f;
3561 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3562 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3563 }
3564 }
3565
3566 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3567 const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3568 if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3569 {
3570 StartLockWheelingWindow(window);
3571 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3572 window = window->ParentWindow;
3573 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3574 {
3575 float max_step = window->InnerRect.GetWidth() * 0.67f;
3576 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3577 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3578 }
3579 }
3580 }
3581
UpdateTabFocus()3582 void ImGui::UpdateTabFocus()
3583 {
3584 ImGuiContext& g = *GImGui;
3585
3586 // Pressing TAB activate widget focus
3587 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3588 if (g.ActiveId == 0 && g.FocusTabPressed)
3589 {
3590 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3591 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3592 g.FocusRequestNextWindow = g.NavWindow;
3593 g.FocusRequestNextCounterRegular = INT_MAX;
3594 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3595 g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3596 else
3597 g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3598 }
3599
3600 // Turn queued focus request into current one
3601 g.FocusRequestCurrWindow = NULL;
3602 g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3603 if (g.FocusRequestNextWindow != NULL)
3604 {
3605 ImGuiWindow* window = g.FocusRequestNextWindow;
3606 g.FocusRequestCurrWindow = window;
3607 if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3608 g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3609 if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3610 g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3611 g.FocusRequestNextWindow = NULL;
3612 g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3613 }
3614
3615 g.NavIdTabCounter = INT_MAX;
3616 }
3617
3618 // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
UpdateHoveredWindowAndCaptureFlags()3619 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3620 {
3621 ImGuiContext& g = *GImGui;
3622
3623 // Find the window hovered by mouse:
3624 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3625 // - 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.
3626 // - 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.
3627 bool clear_hovered_windows = false;
3628 FindHoveredWindow();
3629
3630 // Modal windows prevents mouse from hovering behind them.
3631 ImGuiWindow* modal_window = GetTopMostPopupModal();
3632 if (modal_window && g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3633 clear_hovered_windows = true;
3634
3635 // Disabled mouse?
3636 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3637 clear_hovered_windows = true;
3638
3639 // 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.
3640 int mouse_earliest_button_down = -1;
3641 bool mouse_any_down = false;
3642 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3643 {
3644 if (g.IO.MouseClicked[i])
3645 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);
3646 mouse_any_down |= g.IO.MouseDown[i];
3647 if (g.IO.MouseDown[i])
3648 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3649 mouse_earliest_button_down = i;
3650 }
3651 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3652
3653 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3654 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3655 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3656 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3657 clear_hovered_windows = true;
3658
3659 if (clear_hovered_windows)
3660 g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
3661
3662 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3663 if (g.WantCaptureMouseNextFrame != -1)
3664 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3665 else
3666 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);
3667
3668 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3669 if (g.WantCaptureKeyboardNextFrame != -1)
3670 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3671 else
3672 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3673 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3674 g.IO.WantCaptureKeyboard = true;
3675
3676 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3677 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3678 }
3679
GetMergedKeyModFlags()3680 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags()
3681 {
3682 ImGuiContext& g = *GImGui;
3683 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None;
3684 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; }
3685 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; }
3686 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; }
3687 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; }
3688 return key_mod_flags;
3689 }
3690
NewFrame()3691 void ImGui::NewFrame()
3692 {
3693 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3694 ImGuiContext& g = *GImGui;
3695
3696 #ifdef IMGUI_ENABLE_TEST_ENGINE
3697 ImGuiTestEngineHook_PreNewFrame(&g);
3698 #endif
3699
3700 // Check and assert for various common IO and Configuration mistakes
3701 ErrorCheckNewFrameSanityChecks();
3702
3703 // Load settings on first frame, save settings when modified (after a delay)
3704 UpdateSettings();
3705
3706 g.Time += g.IO.DeltaTime;
3707 g.WithinFrameScope = true;
3708 g.FrameCount += 1;
3709 g.TooltipOverrideCount = 0;
3710 g.WindowsActiveCount = 0;
3711 g.MenusIdSubmittedThisFrame.resize(0);
3712
3713 // Calculate frame-rate for the user, as a purely luxurious feature
3714 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3715 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3716 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3717 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3718
3719 // Setup current font and draw list shared data
3720 g.IO.Fonts->Locked = true;
3721 SetCurrentFont(GetDefaultFont());
3722 IM_ASSERT(g.Font->IsLoaded());
3723 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3724 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3725 g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3726 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3727 if (g.Style.AntiAliasedLines)
3728 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3729 if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))
3730 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
3731 if (g.Style.AntiAliasedFill)
3732 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3733 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3734 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3735
3736 g.BackgroundDrawList._ResetForNewFrame();
3737 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3738 g.BackgroundDrawList.PushClipRectFullScreen();
3739
3740 g.ForegroundDrawList._ResetForNewFrame();
3741 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3742 g.ForegroundDrawList.PushClipRectFullScreen();
3743
3744 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3745 g.DrawData.Clear();
3746
3747 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3748 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3749 KeepAliveID(g.DragDropPayload.SourceId);
3750
3751 // Update HoveredId data
3752 if (!g.HoveredIdPreviousFrame)
3753 g.HoveredIdTimer = 0.0f;
3754 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3755 g.HoveredIdNotActiveTimer = 0.0f;
3756 if (g.HoveredId)
3757 g.HoveredIdTimer += g.IO.DeltaTime;
3758 if (g.HoveredId && g.ActiveId != g.HoveredId)
3759 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3760 g.HoveredIdPreviousFrame = g.HoveredId;
3761 g.HoveredId = 0;
3762 g.HoveredIdAllowOverlap = false;
3763 g.HoveredIdDisabled = false;
3764
3765 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
3766 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3767 ClearActiveID();
3768 if (g.ActiveId)
3769 g.ActiveIdTimer += g.IO.DeltaTime;
3770 g.LastActiveIdTimer += g.IO.DeltaTime;
3771 g.ActiveIdPreviousFrame = g.ActiveId;
3772 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3773 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3774 g.ActiveIdIsAlive = 0;
3775 g.ActiveIdHasBeenEditedThisFrame = false;
3776 g.ActiveIdPreviousFrameIsAlive = false;
3777 g.ActiveIdIsJustActivated = false;
3778 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
3779 g.TempInputId = 0;
3780 if (g.ActiveId == 0)
3781 {
3782 g.ActiveIdUsingNavDirMask = 0x00;
3783 g.ActiveIdUsingNavInputMask = 0x00;
3784 g.ActiveIdUsingKeyInputMask = 0x00;
3785 }
3786
3787 // Drag and drop
3788 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3789 g.DragDropAcceptIdCurr = 0;
3790 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3791 g.DragDropWithinSource = false;
3792 g.DragDropWithinTarget = false;
3793 g.DragDropHoldJustPressedId = 0;
3794
3795 // Update keyboard input state
3796 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
3797 g.IO.KeyMods = GetMergedKeyModFlags();
3798 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3799 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3800 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;
3801
3802 // Update gamepad/keyboard navigation
3803 NavUpdate();
3804
3805 // Update mouse input state
3806 UpdateMouseInputs();
3807
3808 // Find hovered window
3809 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3810 UpdateHoveredWindowAndCaptureFlags();
3811
3812 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3813 UpdateMouseMovingWindowNewFrame();
3814
3815 // Background darkening/whitening
3816 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3817 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3818 else
3819 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3820
3821 g.MouseCursor = ImGuiMouseCursor_Arrow;
3822 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3823 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3824
3825 // Mouse wheel scrolling, scale
3826 UpdateMouseWheel();
3827
3828 // Update legacy TAB focus
3829 UpdateTabFocus();
3830
3831 // Mark all windows as not visible and compact unused memory.
3832 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3833 const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3834 for (int i = 0; i != g.Windows.Size; i++)
3835 {
3836 ImGuiWindow* window = g.Windows[i];
3837 window->WasActive = window->Active;
3838 window->BeginCount = 0;
3839 window->Active = false;
3840 window->WriteAccessed = false;
3841
3842 // Garbage collect transient buffers of recently unused windows
3843 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3844 GcCompactTransientWindowBuffers(window);
3845 }
3846
3847 // Closing the focused window restore focus to the first active root window in descending z-order
3848 if (g.NavWindow && !g.NavWindow->WasActive)
3849 FocusTopMostWindowUnderOne(NULL, NULL);
3850
3851 // No window should be open at the beginning of the frame.
3852 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3853 g.CurrentWindowStack.resize(0);
3854 g.BeginPopupStack.resize(0);
3855 ClosePopupsOverWindow(g.NavWindow, false);
3856
3857 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3858 UpdateDebugToolItemPicker();
3859
3860 // Create implicit/fallback window - which we will only render it if the user has added something to it.
3861 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3862 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3863 g.WithinFrameScopeWithImplicitWindow = true;
3864 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
3865 Begin("Debug##Default");
3866 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
3867
3868 #ifdef IMGUI_ENABLE_TEST_ENGINE
3869 ImGuiTestEngineHook_PostNewFrame(&g);
3870 #endif
3871 }
3872
3873 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3874 void ImGui::UpdateDebugToolItemPicker()
3875 {
3876 ImGuiContext& g = *GImGui;
3877 g.DebugItemPickerBreakId = 0;
3878 if (g.DebugItemPickerActive)
3879 {
3880 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3881 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3882 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3883 g.DebugItemPickerActive = false;
3884 if (ImGui::IsMouseClicked(0) && hovered_id)
3885 {
3886 g.DebugItemPickerBreakId = hovered_id;
3887 g.DebugItemPickerActive = false;
3888 }
3889 ImGui::SetNextWindowBgAlpha(0.60f);
3890 ImGui::BeginTooltip();
3891 ImGui::Text("HoveredId: 0x%08X", hovered_id);
3892 ImGui::Text("Press ESC to abort picking.");
3893 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3894 ImGui::EndTooltip();
3895 }
3896 }
3897
Initialize(ImGuiContext * context)3898 void ImGui::Initialize(ImGuiContext* context)
3899 {
3900 ImGuiContext& g = *context;
3901 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3902
3903 // Add .ini handle for ImGuiWindow type
3904 {
3905 ImGuiSettingsHandler ini_handler;
3906 ini_handler.TypeName = "Window";
3907 ini_handler.TypeHash = ImHashStr("Window");
3908 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
3909 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
3910 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
3911 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
3912 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
3913 g.SettingsHandlers.push_back(ini_handler);
3914 }
3915
3916 #ifdef IMGUI_HAS_TABLE
3917 // Add .ini handle for ImGuiTable type
3918 {
3919 ImGuiSettingsHandler ini_handler;
3920 ini_handler.TypeName = "Table";
3921 ini_handler.TypeHash = ImHashStr("Table");
3922 ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
3923 ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
3924 ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
3925 g.SettingsHandlers.push_back(ini_handler);
3926 }
3927 #endif // #ifdef IMGUI_HAS_TABLE
3928
3929 #ifdef IMGUI_HAS_DOCK
3930 #endif // #ifdef IMGUI_HAS_DOCK
3931
3932 g.Initialized = true;
3933 }
3934
3935 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3936 void ImGui::Shutdown(ImGuiContext* context)
3937 {
3938 // 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)
3939 ImGuiContext& g = *context;
3940 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3941 {
3942 g.IO.Fonts->Locked = false;
3943 IM_DELETE(g.IO.Fonts);
3944 }
3945 g.IO.Fonts = NULL;
3946
3947 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3948 if (!g.Initialized)
3949 return;
3950
3951 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3952 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3953 {
3954 ImGuiContext* backup_context = GImGui;
3955 SetCurrentContext(context);
3956 SaveIniSettingsToDisk(g.IO.IniFilename);
3957 SetCurrentContext(backup_context);
3958 }
3959
3960 // Notify hooked test engine, if any
3961 #ifdef IMGUI_ENABLE_TEST_ENGINE
3962 ImGuiTestEngineHook_Shutdown(context);
3963 #endif
3964
3965 // Clear everything else
3966 for (int i = 0; i < g.Windows.Size; i++)
3967 IM_DELETE(g.Windows[i]);
3968 g.Windows.clear();
3969 g.WindowsFocusOrder.clear();
3970 g.WindowsTempSortBuffer.clear();
3971 g.CurrentWindow = NULL;
3972 g.CurrentWindowStack.clear();
3973 g.WindowsById.Clear();
3974 g.NavWindow = NULL;
3975 g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
3976 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3977 g.MovingWindow = NULL;
3978 g.ColorModifiers.clear();
3979 g.StyleModifiers.clear();
3980 g.FontStack.clear();
3981 g.OpenPopupStack.clear();
3982 g.BeginPopupStack.clear();
3983 g.DrawDataBuilder.ClearFreeMemory();
3984 g.BackgroundDrawList._ClearFreeMemory();
3985 g.ForegroundDrawList._ClearFreeMemory();
3986
3987 g.TabBars.Clear();
3988 g.CurrentTabBarStack.clear();
3989 g.ShrinkWidthBuffer.clear();
3990
3991 g.ClipboardHandlerData.clear();
3992 g.MenusIdSubmittedThisFrame.clear();
3993 g.InputTextState.ClearFreeMemory();
3994
3995 g.SettingsWindows.clear();
3996 g.SettingsHandlers.clear();
3997
3998 if (g.LogFile)
3999 {
4000 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4001 if (g.LogFile != stdout)
4002 #endif
4003 ImFileClose(g.LogFile);
4004 g.LogFile = NULL;
4005 }
4006 g.LogBuffer.clear();
4007
4008 g.Initialized = false;
4009 }
4010
4011 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)4012 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
4013 {
4014 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
4015 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
4016 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
4017 return d;
4018 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
4019 return d;
4020 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
4021 }
4022
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)4023 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
4024 {
4025 out_sorted_windows->push_back(window);
4026 if (window->Active)
4027 {
4028 int count = window->DC.ChildWindows.Size;
4029 if (count > 1)
4030 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
4031 for (int i = 0; i < count; i++)
4032 {
4033 ImGuiWindow* child = window->DC.ChildWindows[i];
4034 if (child->Active)
4035 AddWindowToSortBuffer(out_sorted_windows, child);
4036 }
4037 }
4038 }
4039
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)4040 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
4041 {
4042 // Remove trailing command if unused.
4043 // Technically we could return directly instead of popping, but this make things looks neat in Metrics window as well.
4044 draw_list->_PopUnusedDrawCmd();
4045 if (draw_list->CmdBuffer.Size == 0)
4046 return;
4047
4048 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
4049 // May trigger for you if you are using PrimXXX functions incorrectly.
4050 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
4051 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
4052 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
4053 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
4054
4055 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
4056 // If this assert triggers because you are drawing lots of stuff manually:
4057 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
4058 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
4059 // - If you want large meshes with more than 64K vertices, you can either:
4060 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
4061 // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
4062 // 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.
4063 // (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
4064 // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
4065 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
4066 // Your own engine or render API may use different parameters or function calls to specify index sizes.
4067 // 2 and 4 bytes indices are generally supported by most graphics API.
4068 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
4069 // the 64K limit to split your draw commands in multiple draw lists.
4070 if (sizeof(ImDrawIdx) == 2)
4071 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
4072
4073 out_list->push_back(draw_list);
4074 }
4075
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)4076 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
4077 {
4078 ImGuiContext& g = *GImGui;
4079 g.IO.MetricsRenderWindows++;
4080 AddDrawListToDrawData(out_render_list, window->DrawList);
4081 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4082 {
4083 ImGuiWindow* child = window->DC.ChildWindows[i];
4084 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
4085 AddWindowToDrawData(out_render_list, child);
4086 }
4087 }
4088
4089 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4090 static void AddRootWindowToDrawData(ImGuiWindow* window)
4091 {
4092 ImGuiContext& g = *GImGui;
4093 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4094 AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window);
4095 }
4096
FlattenIntoSingleLayer()4097 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4098 {
4099 int n = Layers[0].Size;
4100 int size = n;
4101 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4102 size += Layers[i].Size;
4103 Layers[0].resize(size);
4104 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4105 {
4106 ImVector<ImDrawList*>& layer = Layers[layer_n];
4107 if (layer.empty())
4108 continue;
4109 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4110 n += layer.Size;
4111 layer.resize(0);
4112 }
4113 }
4114
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)4115 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
4116 {
4117 ImGuiIO& io = ImGui::GetIO();
4118 draw_data->Valid = true;
4119 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4120 draw_data->CmdListsCount = draw_lists->Size;
4121 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4122 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
4123 draw_data->DisplaySize = io.DisplaySize;
4124 draw_data->FramebufferScale = io.DisplayFramebufferScale;
4125 for (int n = 0; n < draw_lists->Size; n++)
4126 {
4127 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4128 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4129 }
4130 }
4131
4132 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
4133 // - When using this function it is sane to ensure that float are perfectly rounded to integer values,
4134 // so that e.g. (int)(max.x-min.x) in user's render produce correct result.
4135 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
4136 // some frequently called functions which to modify both channels and clipping simultaneously tend to use the
4137 // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4138 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4139 {
4140 ImGuiWindow* window = GetCurrentWindow();
4141 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4142 window->ClipRect = window->DrawList->_ClipRectStack.back();
4143 }
4144
PopClipRect()4145 void ImGui::PopClipRect()
4146 {
4147 ImGuiWindow* window = GetCurrentWindow();
4148 window->DrawList->PopClipRect();
4149 window->ClipRect = window->DrawList->_ClipRectStack.back();
4150 }
4151
4152 // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
EndFrame()4153 void ImGui::EndFrame()
4154 {
4155 ImGuiContext& g = *GImGui;
4156 IM_ASSERT(g.Initialized);
4157
4158 // Don't process EndFrame() multiple times.
4159 if (g.FrameCountEnded == g.FrameCount)
4160 return;
4161 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4162
4163 ErrorCheckEndFrameSanityChecks();
4164
4165 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4166 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4167 {
4168 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4169 g.PlatformImeLastPos = g.PlatformImePos;
4170 }
4171
4172 // Hide implicit/fallback "Debug" window if it hasn't been used
4173 g.WithinFrameScopeWithImplicitWindow = false;
4174 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4175 g.CurrentWindow->Active = false;
4176 End();
4177
4178 // Update navigation: CTRL+Tab, wrap-around requests
4179 NavEndFrame();
4180
4181 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4182 if (g.DragDropActive)
4183 {
4184 bool is_delivered = g.DragDropPayload.Delivery;
4185 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4186 if (is_delivered || is_elapsed)
4187 ClearDragDrop();
4188 }
4189
4190 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4191 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
4192 {
4193 g.DragDropWithinSource = true;
4194 SetTooltip("...");
4195 g.DragDropWithinSource = false;
4196 }
4197
4198 // End frame
4199 g.WithinFrameScope = false;
4200 g.FrameCountEnded = g.FrameCount;
4201
4202 // Initiate moving window + handle left-click and right-click focus
4203 UpdateMouseMovingWindowEndFrame();
4204
4205 // Sort the window list so that all child windows are after their parent
4206 // We cannot do that on FocusWindow() because children may not exist yet
4207 g.WindowsTempSortBuffer.resize(0);
4208 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4209 for (int i = 0; i != g.Windows.Size; i++)
4210 {
4211 ImGuiWindow* window = g.Windows[i];
4212 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4213 continue;
4214 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4215 }
4216
4217 // 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.
4218 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4219 g.Windows.swap(g.WindowsTempSortBuffer);
4220 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4221
4222 // Unlock font atlas
4223 g.IO.Fonts->Locked = false;
4224
4225 // Clear Input data for next frame
4226 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4227 g.IO.InputQueueCharacters.resize(0);
4228 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4229 }
4230
Render()4231 void ImGui::Render()
4232 {
4233 ImGuiContext& g = *GImGui;
4234 IM_ASSERT(g.Initialized);
4235
4236 if (g.FrameCountEnded != g.FrameCount)
4237 EndFrame();
4238 g.FrameCountRendered = g.FrameCount;
4239 g.IO.MetricsRenderWindows = 0;
4240 g.DrawDataBuilder.Clear();
4241
4242 // Add background ImDrawList
4243 if (!g.BackgroundDrawList.VtxBuffer.empty())
4244 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4245
4246 // Add ImDrawList to render
4247 ImGuiWindow* windows_to_render_top_most[2];
4248 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4249 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
4250 for (int n = 0; n != g.Windows.Size; n++)
4251 {
4252 ImGuiWindow* window = g.Windows[n];
4253 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4254 AddRootWindowToDrawData(window);
4255 }
4256 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4257 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4258 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4259 g.DrawDataBuilder.FlattenIntoSingleLayer();
4260
4261 // Draw software mouse cursor if requested
4262 if (g.IO.MouseDrawCursor)
4263 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4264
4265 // Add foreground ImDrawList
4266 if (!g.ForegroundDrawList.VtxBuffer.empty())
4267 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4268
4269 // Setup ImDrawData structure for end-user
4270 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4271 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4272 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4273
4274 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4275 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4276 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4277 g.IO.RenderDrawListsFn(&g.DrawData);
4278 #endif
4279 }
4280
4281 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4282 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)4283 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4284 {
4285 ImGuiContext& g = *GImGui;
4286
4287 const char* text_display_end;
4288 if (hide_text_after_double_hash)
4289 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4290 else
4291 text_display_end = text_end;
4292
4293 ImFont* font = g.Font;
4294 const float font_size = g.FontSize;
4295 if (text == text_display_end)
4296 return ImVec2(0.0f, font_size);
4297 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4298
4299 // Round
4300 text_size.x = IM_FLOOR(text_size.x + 0.95f);
4301
4302 return text_size;
4303 }
4304
4305 // Find window given position, search front-to-back
4306 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
4307 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4308 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4309 static void FindHoveredWindow()
4310 {
4311 ImGuiContext& g = *GImGui;
4312
4313 ImGuiWindow* hovered_window = NULL;
4314 ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
4315 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4316 hovered_window = g.MovingWindow;
4317
4318 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4319 ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular;
4320 for (int i = g.Windows.Size - 1; i >= 0; i--)
4321 {
4322 ImGuiWindow* window = g.Windows[i];
4323 if (!window->Active || window->Hidden)
4324 continue;
4325 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4326 continue;
4327
4328 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4329 ImRect bb(window->OuterRectClipped);
4330 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4331 bb.Expand(padding_regular);
4332 else
4333 bb.Expand(padding_for_resize_from_edges);
4334 if (!bb.Contains(g.IO.MousePos))
4335 continue;
4336
4337 // Support for one rectangular hole in any given window
4338 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
4339 if (window->HitTestHoleSize.x != 0)
4340 {
4341 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
4342 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
4343 if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))
4344 continue;
4345 }
4346
4347 if (hovered_window == NULL)
4348 hovered_window = window;
4349 if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
4350 hovered_window_ignoring_moving_window = window;
4351 if (hovered_window && hovered_window_ignoring_moving_window)
4352 break;
4353 }
4354
4355 g.HoveredWindow = hovered_window;
4356 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4357 g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
4358 }
4359
4360 // Test if mouse cursor is hovering given rectangle
4361 // NB- Rectangle is clipped by our current clip setting
4362 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
IsMouseHoveringRect(const ImVec2 & r_min,const ImVec2 & r_max,bool clip)4363 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4364 {
4365 ImGuiContext& g = *GImGui;
4366
4367 // Clip
4368 ImRect rect_clipped(r_min, r_max);
4369 if (clip)
4370 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4371
4372 // Expand for touch input
4373 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4374 if (!rect_for_touch.Contains(g.IO.MousePos))
4375 return false;
4376 return true;
4377 }
4378
GetKeyIndex(ImGuiKey imgui_key)4379 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4380 {
4381 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4382 ImGuiContext& g = *GImGui;
4383 return g.IO.KeyMap[imgui_key];
4384 }
4385
4386 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4387 // Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4388 bool ImGui::IsKeyDown(int user_key_index)
4389 {
4390 if (user_key_index < 0)
4391 return false;
4392 ImGuiContext& g = *GImGui;
4393 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4394 return g.IO.KeysDown[user_key_index];
4395 }
4396
4397 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4398 // t1 = current time (e.g.: g.Time)
4399 // An event is triggered at:
4400 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4401 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4402 {
4403 if (t1 == 0.0f)
4404 return 1;
4405 if (t0 >= t1)
4406 return 0;
4407 if (repeat_rate <= 0.0f)
4408 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4409 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4410 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4411 const int count = count_t1 - count_t0;
4412 return count;
4413 }
4414
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4415 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4416 {
4417 ImGuiContext& g = *GImGui;
4418 if (key_index < 0)
4419 return 0;
4420 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4421 const float t = g.IO.KeysDownDuration[key_index];
4422 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4423 }
4424
IsKeyPressed(int user_key_index,bool repeat)4425 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4426 {
4427 ImGuiContext& g = *GImGui;
4428 if (user_key_index < 0)
4429 return false;
4430 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4431 const float t = g.IO.KeysDownDuration[user_key_index];
4432 if (t == 0.0f)
4433 return true;
4434 if (repeat && t > g.IO.KeyRepeatDelay)
4435 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4436 return false;
4437 }
4438
IsKeyReleased(int user_key_index)4439 bool ImGui::IsKeyReleased(int user_key_index)
4440 {
4441 ImGuiContext& g = *GImGui;
4442 if (user_key_index < 0) return false;
4443 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4444 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4445 }
4446
IsMouseDown(ImGuiMouseButton button)4447 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4448 {
4449 ImGuiContext& g = *GImGui;
4450 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4451 return g.IO.MouseDown[button];
4452 }
4453
IsMouseClicked(ImGuiMouseButton button,bool repeat)4454 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4455 {
4456 ImGuiContext& g = *GImGui;
4457 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4458 const float t = g.IO.MouseDownDuration[button];
4459 if (t == 0.0f)
4460 return true;
4461
4462 if (repeat && t > g.IO.KeyRepeatDelay)
4463 {
4464 // 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.
4465 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4466 if (amount > 0)
4467 return true;
4468 }
4469 return false;
4470 }
4471
IsMouseReleased(ImGuiMouseButton button)4472 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4473 {
4474 ImGuiContext& g = *GImGui;
4475 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4476 return g.IO.MouseReleased[button];
4477 }
4478
IsMouseDoubleClicked(ImGuiMouseButton button)4479 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4480 {
4481 ImGuiContext& g = *GImGui;
4482 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4483 return g.IO.MouseDoubleClicked[button];
4484 }
4485
4486 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
4487 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4488 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4489 {
4490 ImGuiContext& g = *GImGui;
4491 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4492 if (lock_threshold < 0.0f)
4493 lock_threshold = g.IO.MouseDragThreshold;
4494 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4495 }
4496
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4497 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4498 {
4499 ImGuiContext& g = *GImGui;
4500 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4501 if (!g.IO.MouseDown[button])
4502 return false;
4503 return IsMouseDragPastThreshold(button, lock_threshold);
4504 }
4505
GetMousePos()4506 ImVec2 ImGui::GetMousePos()
4507 {
4508 ImGuiContext& g = *GImGui;
4509 return g.IO.MousePos;
4510 }
4511
4512 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4513 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4514 {
4515 ImGuiContext& g = *GImGui;
4516 if (g.BeginPopupStack.Size > 0)
4517 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
4518 return g.IO.MousePos;
4519 }
4520
4521 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4522 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4523 {
4524 // The assert is only to silence a false-positive in XCode Static Analysis.
4525 // 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).
4526 IM_ASSERT(GImGui != NULL);
4527 const float MOUSE_INVALID = -256000.0f;
4528 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4529 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4530 }
4531
IsAnyMouseDown()4532 bool ImGui::IsAnyMouseDown()
4533 {
4534 ImGuiContext& g = *GImGui;
4535 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4536 if (g.IO.MouseDown[n])
4537 return true;
4538 return false;
4539 }
4540
4541 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4542 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4543 // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window.
GetMouseDragDelta(ImGuiMouseButton button,float lock_threshold)4544 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4545 {
4546 ImGuiContext& g = *GImGui;
4547 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4548 if (lock_threshold < 0.0f)
4549 lock_threshold = g.IO.MouseDragThreshold;
4550 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4551 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4552 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4553 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4554 return ImVec2(0.0f, 0.0f);
4555 }
4556
ResetMouseDragDelta(ImGuiMouseButton button)4557 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4558 {
4559 ImGuiContext& g = *GImGui;
4560 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4561 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4562 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4563 }
4564
GetMouseCursor()4565 ImGuiMouseCursor ImGui::GetMouseCursor()
4566 {
4567 return GImGui->MouseCursor;
4568 }
4569
SetMouseCursor(ImGuiMouseCursor cursor_type)4570 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4571 {
4572 GImGui->MouseCursor = cursor_type;
4573 }
4574
CaptureKeyboardFromApp(bool capture)4575 void ImGui::CaptureKeyboardFromApp(bool capture)
4576 {
4577 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4578 }
4579
CaptureMouseFromApp(bool capture)4580 void ImGui::CaptureMouseFromApp(bool capture)
4581 {
4582 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4583 }
4584
IsItemActive()4585 bool ImGui::IsItemActive()
4586 {
4587 ImGuiContext& g = *GImGui;
4588 if (g.ActiveId)
4589 {
4590 ImGuiWindow* window = g.CurrentWindow;
4591 return g.ActiveId == window->DC.LastItemId;
4592 }
4593 return false;
4594 }
4595
IsItemActivated()4596 bool ImGui::IsItemActivated()
4597 {
4598 ImGuiContext& g = *GImGui;
4599 if (g.ActiveId)
4600 {
4601 ImGuiWindow* window = g.CurrentWindow;
4602 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4603 return true;
4604 }
4605 return false;
4606 }
4607
IsItemDeactivated()4608 bool ImGui::IsItemDeactivated()
4609 {
4610 ImGuiContext& g = *GImGui;
4611 ImGuiWindow* window = g.CurrentWindow;
4612 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4613 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4614 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4615 }
4616
IsItemDeactivatedAfterEdit()4617 bool ImGui::IsItemDeactivatedAfterEdit()
4618 {
4619 ImGuiContext& g = *GImGui;
4620 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4621 }
4622
IsItemFocused()4623 bool ImGui::IsItemFocused()
4624 {
4625 ImGuiContext& g = *GImGui;
4626 ImGuiWindow* window = g.CurrentWindow;
4627
4628 if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4629 return false;
4630 return true;
4631 }
4632
IsItemClicked(ImGuiMouseButton mouse_button)4633 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4634 {
4635 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4636 }
4637
IsItemToggledOpen()4638 bool ImGui::IsItemToggledOpen()
4639 {
4640 ImGuiContext& g = *GImGui;
4641 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4642 }
4643
IsItemToggledSelection()4644 bool ImGui::IsItemToggledSelection()
4645 {
4646 ImGuiContext& g = *GImGui;
4647 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4648 }
4649
IsAnyItemHovered()4650 bool ImGui::IsAnyItemHovered()
4651 {
4652 ImGuiContext& g = *GImGui;
4653 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4654 }
4655
IsAnyItemActive()4656 bool ImGui::IsAnyItemActive()
4657 {
4658 ImGuiContext& g = *GImGui;
4659 return g.ActiveId != 0;
4660 }
4661
IsAnyItemFocused()4662 bool ImGui::IsAnyItemFocused()
4663 {
4664 ImGuiContext& g = *GImGui;
4665 return g.NavId != 0 && !g.NavDisableHighlight;
4666 }
4667
IsItemVisible()4668 bool ImGui::IsItemVisible()
4669 {
4670 ImGuiWindow* window = GetCurrentWindowRead();
4671 return window->ClipRect.Overlaps(window->DC.LastItemRect);
4672 }
4673
IsItemEdited()4674 bool ImGui::IsItemEdited()
4675 {
4676 ImGuiWindow* window = GetCurrentWindowRead();
4677 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4678 }
4679
4680 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
SetItemAllowOverlap()4681 void ImGui::SetItemAllowOverlap()
4682 {
4683 ImGuiContext& g = *GImGui;
4684 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4685 g.HoveredIdAllowOverlap = true;
4686 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4687 g.ActiveIdAllowOverlap = true;
4688 }
4689
GetItemRectMin()4690 ImVec2 ImGui::GetItemRectMin()
4691 {
4692 ImGuiWindow* window = GetCurrentWindowRead();
4693 return window->DC.LastItemRect.Min;
4694 }
4695
GetItemRectMax()4696 ImVec2 ImGui::GetItemRectMax()
4697 {
4698 ImGuiWindow* window = GetCurrentWindowRead();
4699 return window->DC.LastItemRect.Max;
4700 }
4701
GetItemRectSize()4702 ImVec2 ImGui::GetItemRectSize()
4703 {
4704 ImGuiWindow* window = GetCurrentWindowRead();
4705 return window->DC.LastItemRect.GetSize();
4706 }
4707
GetViewportRect()4708 static ImRect GetViewportRect()
4709 {
4710 ImGuiContext& g = *GImGui;
4711 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4712 }
4713
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4714 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4715 {
4716 ImGuiContext& g = *GImGui;
4717 ImGuiWindow* parent_window = g.CurrentWindow;
4718
4719 flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow;
4720 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4721
4722 // Size
4723 const ImVec2 content_avail = GetContentRegionAvail();
4724 ImVec2 size = ImFloor(size_arg);
4725 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4726 if (size.x <= 0.0f)
4727 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4728 if (size.y <= 0.0f)
4729 size.y = ImMax(content_avail.y + size.y, 4.0f);
4730 SetNextWindowSize(size);
4731
4732 // 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.
4733 char title[256];
4734 if (name)
4735 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4736 else
4737 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4738
4739 const float backup_border_size = g.Style.ChildBorderSize;
4740 if (!border)
4741 g.Style.ChildBorderSize = 0.0f;
4742 bool ret = Begin(title, NULL, flags);
4743 g.Style.ChildBorderSize = backup_border_size;
4744
4745 ImGuiWindow* child_window = g.CurrentWindow;
4746 child_window->ChildId = id;
4747 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4748
4749 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4750 // While this is not really documented/defined, it seems that the expected thing to do.
4751 if (child_window->BeginCount == 1)
4752 parent_window->DC.CursorPos = child_window->Pos;
4753
4754 // Process navigation-in immediately so NavInit can run on first frame
4755 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4756 {
4757 FocusWindow(child_window);
4758 NavInitWindow(child_window, false);
4759 SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
4760 g.ActiveIdSource = ImGuiInputSource_Nav;
4761 }
4762 return ret;
4763 }
4764
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4765 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4766 {
4767 ImGuiWindow* window = GetCurrentWindow();
4768 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4769 }
4770
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4771 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4772 {
4773 IM_ASSERT(id != 0);
4774 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4775 }
4776
EndChild()4777 void ImGui::EndChild()
4778 {
4779 ImGuiContext& g = *GImGui;
4780 ImGuiWindow* window = g.CurrentWindow;
4781
4782 IM_ASSERT(g.WithinEndChild == false);
4783 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
4784
4785 g.WithinEndChild = true;
4786 if (window->BeginCount > 1)
4787 {
4788 End();
4789 }
4790 else
4791 {
4792 ImVec2 sz = window->Size;
4793 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4794 sz.x = ImMax(4.0f, sz.x);
4795 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4796 sz.y = ImMax(4.0f, sz.y);
4797 End();
4798
4799 ImGuiWindow* parent_window = g.CurrentWindow;
4800 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4801 ItemSize(sz);
4802 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4803 {
4804 ItemAdd(bb, window->ChildId);
4805 RenderNavHighlight(bb, window->ChildId);
4806
4807 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4808 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4809 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4810 }
4811 else
4812 {
4813 // Not navigable into
4814 ItemAdd(bb, 0);
4815 }
4816 }
4817 g.WithinEndChild = false;
4818 }
4819
4820 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4821 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4822 {
4823 ImGuiContext& g = *GImGui;
4824 const ImGuiStyle& style = g.Style;
4825 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4826 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4827 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4828 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4829 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4830 PopStyleVar(3);
4831 PopStyleColor();
4832 return ret;
4833 }
4834
EndChildFrame()4835 void ImGui::EndChildFrame()
4836 {
4837 EndChild();
4838 }
4839
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4840 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4841 {
4842 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4843 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4844 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4845 }
4846
FindWindowByID(ImGuiID id)4847 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4848 {
4849 ImGuiContext& g = *GImGui;
4850 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4851 }
4852
FindWindowByName(const char * name)4853 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4854 {
4855 ImGuiID id = ImHashStr(name);
4856 return FindWindowByID(id);
4857 }
4858
ApplyWindowSettings(ImGuiWindow * window,ImGuiWindowSettings * settings)4859 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
4860 {
4861 window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y));
4862 if (settings->Size.x > 0 && settings->Size.y > 0)
4863 window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y));
4864 window->Collapsed = settings->Collapsed;
4865 }
4866
CreateNewWindow(const char * name,ImGuiWindowFlags flags)4867 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
4868 {
4869 ImGuiContext& g = *GImGui;
4870 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4871
4872 // Create window the first time
4873 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4874 window->Flags = flags;
4875 g.WindowsById.SetVoidPtr(window->ID, window);
4876
4877 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4878 window->Pos = ImVec2(60, 60);
4879
4880 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4881 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4882 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4883 {
4884 // Retrieve settings from .ini file
4885 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4886 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4887 ApplyWindowSettings(window, settings);
4888 }
4889 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4890
4891 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4892 {
4893 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4894 window->AutoFitOnlyGrows = false;
4895 }
4896 else
4897 {
4898 if (window->Size.x <= 0.0f)
4899 window->AutoFitFramesX = 2;
4900 if (window->Size.y <= 0.0f)
4901 window->AutoFitFramesY = 2;
4902 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4903 }
4904
4905 g.WindowsFocusOrder.push_back(window);
4906 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4907 g.Windows.push_front(window); // Quite slow but rare and only once
4908 else
4909 g.Windows.push_back(window);
4910 return window;
4911 }
4912
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4913 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4914 {
4915 ImGuiContext& g = *GImGui;
4916 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4917 {
4918 // Using -1,-1 on either X/Y axis to preserve the current size.
4919 ImRect cr = g.NextWindowData.SizeConstraintRect;
4920 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4921 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4922 if (g.NextWindowData.SizeCallback)
4923 {
4924 ImGuiSizeCallbackData data;
4925 data.UserData = g.NextWindowData.SizeCallbackUserData;
4926 data.Pos = window->Pos;
4927 data.CurrentSize = window->SizeFull;
4928 data.DesiredSize = new_size;
4929 g.NextWindowData.SizeCallback(&data);
4930 new_size = data.DesiredSize;
4931 }
4932 new_size.x = IM_FLOOR(new_size.x);
4933 new_size.y = IM_FLOOR(new_size.y);
4934 }
4935
4936 // Minimum size
4937 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4938 {
4939 ImGuiWindow* window_for_height = window;
4940 new_size = ImMax(new_size, g.Style.WindowMinSize);
4941 new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4942 }
4943 return new_size;
4944 }
4945
CalcWindowContentSize(ImGuiWindow * window)4946 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4947 {
4948 if (window->Collapsed)
4949 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4950 return window->ContentSize;
4951 if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4952 return window->ContentSize;
4953
4954 ImVec2 sz;
4955 sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4956 sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4957 return sz;
4958 }
4959
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4960 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4961 {
4962 ImGuiContext& g = *GImGui;
4963 ImGuiStyle& style = g.Style;
4964 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4965 ImVec2 size_pad = window->WindowPadding * 2.0f;
4966 ImVec2 size_desired = size_contents + size_pad + size_decorations;
4967 if (window->Flags & ImGuiWindowFlags_Tooltip)
4968 {
4969 // Tooltip always resize
4970 return size_desired;
4971 }
4972 else
4973 {
4974 // Maximum window size is determined by the viewport size or monitor size
4975 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4976 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4977 ImVec2 size_min = style.WindowMinSize;
4978 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)
4979 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4980 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4981
4982 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4983 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4984 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4985 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
4986 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
4987 if (will_have_scrollbar_x)
4988 size_auto_fit.y += style.ScrollbarSize;
4989 if (will_have_scrollbar_y)
4990 size_auto_fit.x += style.ScrollbarSize;
4991 return size_auto_fit;
4992 }
4993 }
4994
CalcWindowExpectedSize(ImGuiWindow * window)4995 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4996 {
4997 ImVec2 size_contents = CalcWindowContentSize(window);
4998 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4999 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5000 return size_final;
5001 }
5002
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)5003 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
5004 {
5005 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
5006 return ImGuiCol_PopupBg;
5007 if (flags & ImGuiWindowFlags_ChildWindow)
5008 return ImGuiCol_ChildBg;
5009 return ImGuiCol_WindowBg;
5010 }
5011
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)5012 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
5013 {
5014 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
5015 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
5016 ImVec2 size_expected = pos_max - pos_min;
5017 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
5018 *out_pos = pos_min;
5019 if (corner_norm.x == 0.0f)
5020 out_pos->x -= (size_constrained.x - size_expected.x);
5021 if (corner_norm.y == 0.0f)
5022 out_pos->y -= (size_constrained.y - size_expected.y);
5023 *out_size = size_constrained;
5024 }
5025
5026 struct ImGuiResizeGripDef
5027 {
5028 ImVec2 CornerPosN;
5029 ImVec2 InnerDir;
5030 int AngleMin12, AngleMax12;
5031 };
5032
5033 static const ImGuiResizeGripDef resize_grip_def[4] =
5034 {
5035 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
5036 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
5037 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
5038 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }, // Upper-right (Unused)
5039 };
5040
5041 struct ImGuiResizeBorderDef
5042 {
5043 ImVec2 InnerDir;
5044 ImVec2 CornerPosN1, CornerPosN2;
5045 float OuterAngle;
5046 };
5047
5048 static const ImGuiResizeBorderDef resize_border_def[4] =
5049 {
5050 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Top
5051 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
5052 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }, // Bottom
5053 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f } // Left
5054 };
5055
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)5056 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
5057 {
5058 ImRect rect = window->Rect();
5059 if (thickness == 0.0f) rect.Max -= ImVec2(1, 1);
5060 if (border_n == 0) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } // Top
5061 if (border_n == 1) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } // Right
5062 if (border_n == 2) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } // Bottom
5063 if (border_n == 3) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } // Left
5064 IM_ASSERT(0);
5065 return ImRect();
5066 }
5067
5068 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
5069 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)5070 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
5071 {
5072 IM_ASSERT(n >= 0 && n <= 7);
5073 ImGuiID id = window->ID;
5074 id = ImHashStr("#RESIZE", 0, id);
5075 id = ImHashData(&n, sizeof(int), id);
5076 return id;
5077 }
5078
5079 // Handle resize for: Resize Grips, Borders, Gamepad
5080 // Return true when using auto-fit (double click on resize grip)
UpdateWindowManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4],const ImRect & visibility_rect)5081 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
5082 {
5083 ImGuiContext& g = *GImGui;
5084 ImGuiWindowFlags flags = window->Flags;
5085
5086 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5087 return false;
5088 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
5089 return false;
5090
5091 bool ret_auto_fit = false;
5092 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
5093 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5094 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
5095 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
5096
5097 ImVec2 pos_target(FLT_MAX, FLT_MAX);
5098 ImVec2 size_target(FLT_MAX, FLT_MAX);
5099
5100 // Resize grips and borders are on layer 1
5101 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5102 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5103
5104 // Manual resize grips
5105 PushID("#RESIZE");
5106 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5107 {
5108 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5109 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5110
5111 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5112 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5113 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5114 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5115 bool hovered, held;
5116 ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5117 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5118 if (hovered || held)
5119 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5120
5121 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5122 {
5123 // Manual auto-fit when double-clicking
5124 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5125 ret_auto_fit = true;
5126 ClearActiveID();
5127 }
5128 else if (held)
5129 {
5130 // Resize from any of the four corners
5131 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5132 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip
5133 ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX);
5134 ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX);
5135 corner_target = ImClamp(corner_target, clamp_min, clamp_max);
5136 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5137 }
5138 if (resize_grip_n == 0 || held || hovered)
5139 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5140 }
5141 for (int border_n = 0; border_n < resize_border_count; border_n++)
5142 {
5143 bool hovered, held;
5144 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5145 ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5146 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5147 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5148 {
5149 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5150 if (held)
5151 *border_held = border_n;
5152 }
5153 if (held)
5154 {
5155 ImVec2 border_target = window->Pos;
5156 ImVec2 border_posn;
5157 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top
5158 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right
5159 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom
5160 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left
5161 ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX);
5162 ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX);
5163 border_target = ImClamp(border_target, clamp_min, clamp_max);
5164 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5165 }
5166 }
5167 PopID();
5168
5169 // Restore nav layer
5170 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5171 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5172
5173 // Navigation resize (keyboard/gamepad)
5174 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5175 {
5176 ImVec2 nav_resize_delta;
5177 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5178 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5179 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5180 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5181 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5182 {
5183 const float NAV_RESIZE_SPEED = 600.0f;
5184 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5185 nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size);
5186 g.NavWindowingToggleLayer = false;
5187 g.NavDisableMouseHover = true;
5188 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5189 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5190 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5191 }
5192 }
5193
5194 // Apply back modified position/size to window
5195 if (size_target.x != FLT_MAX)
5196 {
5197 window->SizeFull = size_target;
5198 MarkIniSettingsDirty(window);
5199 }
5200 if (pos_target.x != FLT_MAX)
5201 {
5202 window->Pos = ImFloor(pos_target);
5203 MarkIniSettingsDirty(window);
5204 }
5205
5206 window->Size = window->SizeFull;
5207 return ret_auto_fit;
5208 }
5209
ClampWindowRect(ImGuiWindow * window,const ImRect & visibility_rect)5210 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect)
5211 {
5212 ImGuiContext& g = *GImGui;
5213 ImVec2 size_for_clamping = window->Size;
5214 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5215 size_for_clamping.y = window->TitleBarHeight();
5216 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
5217 }
5218
RenderWindowOuterBorders(ImGuiWindow * window)5219 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5220 {
5221 ImGuiContext& g = *GImGui;
5222 float rounding = window->WindowRounding;
5223 float border_size = window->WindowBorderSize;
5224 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5225 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5226
5227 int border_held = window->ResizeBorderHeld;
5228 if (border_held != -1)
5229 {
5230 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5231 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5232 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
5233 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
5234 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5235 }
5236 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5237 {
5238 float y = window->Pos.y + window->TitleBarHeight() - 1;
5239 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);
5240 }
5241 }
5242
5243 // Draw background and borders
5244 // Draw and handle scrollbars
RenderWindowDecorations(ImGuiWindow * window,const ImRect & title_bar_rect,bool title_bar_is_highlight,int resize_grip_count,const ImU32 resize_grip_col[4],float resize_grip_draw_size)5245 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
5246 {
5247 ImGuiContext& g = *GImGui;
5248 ImGuiStyle& style = g.Style;
5249 ImGuiWindowFlags flags = window->Flags;
5250
5251 // Ensure that ScrollBar doesn't read last frame's SkipItems
5252 IM_ASSERT(window->BeginCount == 0);
5253 window->SkipItems = false;
5254
5255 // Draw window + handle manual resize
5256 // 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.
5257 const float window_rounding = window->WindowRounding;
5258 const float window_border_size = window->WindowBorderSize;
5259 if (window->Collapsed)
5260 {
5261 // Title bar only
5262 float backup_border_size = style.FrameBorderSize;
5263 g.Style.FrameBorderSize = window->WindowBorderSize;
5264 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5265 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5266 g.Style.FrameBorderSize = backup_border_size;
5267 }
5268 else
5269 {
5270 // Window background
5271 if (!(flags & ImGuiWindowFlags_NoBackground))
5272 {
5273 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5274 bool override_alpha = false;
5275 float alpha = 1.0f;
5276 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5277 {
5278 alpha = g.NextWindowData.BgAlphaVal;
5279 override_alpha = true;
5280 }
5281 if (override_alpha)
5282 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5283 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5284 }
5285
5286 // Title bar
5287 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5288 {
5289 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5290 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5291 }
5292
5293 // Menu bar
5294 if (flags & ImGuiWindowFlags_MenuBar)
5295 {
5296 ImRect menu_bar_rect = window->MenuBarRect();
5297 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.
5298 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, ImDrawCornerFlags_Top);
5299 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5300 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5301 }
5302
5303 // Scrollbars
5304 if (window->ScrollbarX)
5305 Scrollbar(ImGuiAxis_X);
5306 if (window->ScrollbarY)
5307 Scrollbar(ImGuiAxis_Y);
5308
5309 // Render resize grips (after their input handling so we don't have a frame of latency)
5310 if (!(flags & ImGuiWindowFlags_NoResize))
5311 {
5312 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5313 {
5314 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5315 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5316 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)));
5317 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)));
5318 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);
5319 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5320 }
5321 }
5322
5323 // Borders
5324 RenderWindowOuterBorders(window);
5325 }
5326 }
5327
5328 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5329 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5330 {
5331 ImGuiContext& g = *GImGui;
5332 ImGuiStyle& style = g.Style;
5333 ImGuiWindowFlags flags = window->Flags;
5334
5335 const bool has_close_button = (p_open != NULL);
5336 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5337
5338 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5339 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5340 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5341 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5342 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5343
5344 // Layout buttons
5345 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5346 float pad_l = style.FramePadding.x;
5347 float pad_r = style.FramePadding.x;
5348 float button_sz = g.FontSize;
5349 ImVec2 close_button_pos;
5350 ImVec2 collapse_button_pos;
5351 if (has_close_button)
5352 {
5353 pad_r += button_sz;
5354 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5355 }
5356 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5357 {
5358 pad_r += button_sz;
5359 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5360 }
5361 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5362 {
5363 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5364 pad_l += button_sz;
5365 }
5366
5367 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5368 if (has_collapse_button)
5369 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5370 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5371
5372 // Close button
5373 if (has_close_button)
5374 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5375 *p_open = false;
5376
5377 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5378 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5379 window->DC.ItemFlags = item_flags_backup;
5380
5381 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5382 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5383 const char* UNSAVED_DOCUMENT_MARKER = "*";
5384 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5385 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5386
5387 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5388 // while uncentered title text will still reach edges correct.
5389 if (pad_l > style.FramePadding.x)
5390 pad_l += g.Style.ItemInnerSpacing.x;
5391 if (pad_r > style.FramePadding.x)
5392 pad_r += g.Style.ItemInnerSpacing.x;
5393 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5394 {
5395 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5396 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5397 pad_l = ImMax(pad_l, pad_extend * centerness);
5398 pad_r = ImMax(pad_r, pad_extend * centerness);
5399 }
5400
5401 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);
5402 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5403 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5404 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5405 if (flags & ImGuiWindowFlags_UnsavedDocument)
5406 {
5407 ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5408 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5409 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5410 }
5411 }
5412
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5413 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5414 {
5415 window->ParentWindow = parent_window;
5416 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5417 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5418 window->RootWindow = parent_window->RootWindow;
5419 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5420 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5421 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5422 {
5423 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5424 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5425 }
5426 }
5427
5428 // Push a new Dear ImGui window to add widgets to.
5429 // - 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.
5430 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5431 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5432 // 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.
5433 // - 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.
5434 // - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
Begin(const char * name,bool * p_open,ImGuiWindowFlags flags)5435 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5436 {
5437 ImGuiContext& g = *GImGui;
5438 const ImGuiStyle& style = g.Style;
5439 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5440 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
5441 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5442
5443 // Find or create
5444 ImGuiWindow* window = FindWindowByName(name);
5445 const bool window_just_created = (window == NULL);
5446 if (window_just_created)
5447 window = CreateNewWindow(name, flags);
5448
5449 // Automatically disable manual moving/resizing when NoInputs is set
5450 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5451 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5452
5453 if (flags & ImGuiWindowFlags_NavFlattened)
5454 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5455
5456 const int current_frame = g.FrameCount;
5457 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5458 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5459
5460 // Update the Appearing flag
5461 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5462 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5463 if (flags & ImGuiWindowFlags_Popup)
5464 {
5465 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5466 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5467 window_just_activated_by_user |= (window != popup_ref.Window);
5468 }
5469 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5470 if (window->Appearing)
5471 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5472
5473 // Update Flags, LastFrameActive, BeginOrderXXX fields
5474 if (first_begin_of_the_frame)
5475 {
5476 window->Flags = (ImGuiWindowFlags)flags;
5477 window->LastFrameActive = current_frame;
5478 window->LastTimeActive = (float)g.Time;
5479 window->BeginOrderWithinParent = 0;
5480 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5481 }
5482 else
5483 {
5484 flags = window->Flags;
5485 }
5486
5487 // 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
5488 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5489 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5490 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5491
5492 // We allow window memory to be compacted so recreate the base stack when needed.
5493 if (window->IDStack.Size == 0)
5494 window->IDStack.push_back(window->ID);
5495
5496 // Add to stack
5497 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5498 g.CurrentWindowStack.push_back(window);
5499 g.CurrentWindow = NULL;
5500 ErrorCheckBeginEndCompareStacksSize(window, true);
5501 if (flags & ImGuiWindowFlags_Popup)
5502 {
5503 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5504 popup_ref.Window = window;
5505 g.BeginPopupStack.push_back(popup_ref);
5506 window->PopupId = popup_ref.PopupId;
5507 }
5508
5509 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5510 window->NavLastIds[0] = 0;
5511
5512 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5513 if (first_begin_of_the_frame)
5514 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5515
5516 // Process SetNextWindow***() calls
5517 // (FIXME: Consider splitting the HasXXX flags into X/Y components
5518 bool window_pos_set_by_api = false;
5519 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5520 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5521 {
5522 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5523 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5524 {
5525 // May be processed on the next frame if this is our first frame and we are measuring size
5526 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5527 window->SetWindowPosVal = g.NextWindowData.PosVal;
5528 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5529 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5530 }
5531 else
5532 {
5533 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5534 }
5535 }
5536 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5537 {
5538 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5539 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5540 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5541 }
5542 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)
5543 {
5544 if (g.NextWindowData.ScrollVal.x >= 0.0f)
5545 {
5546 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
5547 window->ScrollTargetCenterRatio.x = 0.0f;
5548 }
5549 if (g.NextWindowData.ScrollVal.y >= 0.0f)
5550 {
5551 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
5552 window->ScrollTargetCenterRatio.y = 0.0f;
5553 }
5554 }
5555 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5556 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5557 else if (first_begin_of_the_frame)
5558 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5559 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5560 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5561 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5562 FocusWindow(window);
5563 if (window->Appearing)
5564 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5565
5566 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5567 if (first_begin_of_the_frame)
5568 {
5569 // Initialize
5570 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5571 window->Active = true;
5572 window->HasCloseButton = (p_open != NULL);
5573 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
5574 window->IDStack.resize(1);
5575 window->DrawList->_ResetForNewFrame();
5576
5577 // Restore buffer capacity when woken from a compacted state, to avoid
5578 if (window->MemoryCompacted)
5579 GcAwakeTransientWindowBuffers(window);
5580
5581 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5582 // 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.
5583 bool window_title_visible_elsewhere = false;
5584 if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5585 window_title_visible_elsewhere = true;
5586 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5587 {
5588 size_t buf_len = (size_t)window->NameBufLen;
5589 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5590 window->NameBufLen = (int)buf_len;
5591 }
5592
5593 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5594
5595 // Update contents size from last frame for auto-fitting (or use explicit size)
5596 window->ContentSize = CalcWindowContentSize(window);
5597 if (window->HiddenFramesCanSkipItems > 0)
5598 window->HiddenFramesCanSkipItems--;
5599 if (window->HiddenFramesCannotSkipItems > 0)
5600 window->HiddenFramesCannotSkipItems--;
5601
5602 // Hide new windows for one frame until they calculate their size
5603 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5604 window->HiddenFramesCannotSkipItems = 1;
5605
5606 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5607 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5608 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5609 {
5610 window->HiddenFramesCannotSkipItems = 1;
5611 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5612 {
5613 if (!window_size_x_set_by_api)
5614 window->Size.x = window->SizeFull.x = 0.f;
5615 if (!window_size_y_set_by_api)
5616 window->Size.y = window->SizeFull.y = 0.f;
5617 window->ContentSize = ImVec2(0.f, 0.f);
5618 }
5619 }
5620
5621 // SELECT VIEWPORT
5622 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5623 SetCurrentWindow(window);
5624
5625 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5626
5627 if (flags & ImGuiWindowFlags_ChildWindow)
5628 window->WindowBorderSize = style.ChildBorderSize;
5629 else
5630 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5631 window->WindowPadding = style.WindowPadding;
5632 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5633 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5634
5635 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
5636 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5637 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5638
5639 // Collapse window by double-clicking on title bar
5640 // 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
5641 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5642 {
5643 // 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.
5644 ImRect title_bar_rect = window->TitleBarRect();
5645 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5646 window->WantCollapseToggle = true;
5647 if (window->WantCollapseToggle)
5648 {
5649 window->Collapsed = !window->Collapsed;
5650 MarkIniSettingsDirty(window);
5651 FocusWindow(window);
5652 }
5653 }
5654 else
5655 {
5656 window->Collapsed = false;
5657 }
5658 window->WantCollapseToggle = false;
5659
5660 // SIZE
5661
5662 // Calculate auto-fit size, handle automatic resize
5663 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5664 bool use_current_size_for_scrollbar_x = window_just_created;
5665 bool use_current_size_for_scrollbar_y = window_just_created;
5666 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5667 {
5668 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5669 if (!window_size_x_set_by_api)
5670 {
5671 window->SizeFull.x = size_auto_fit.x;
5672 use_current_size_for_scrollbar_x = true;
5673 }
5674 if (!window_size_y_set_by_api)
5675 {
5676 window->SizeFull.y = size_auto_fit.y;
5677 use_current_size_for_scrollbar_y = true;
5678 }
5679 }
5680 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5681 {
5682 // Auto-fit may only grow window during the first few frames
5683 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5684 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5685 {
5686 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5687 use_current_size_for_scrollbar_x = true;
5688 }
5689 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5690 {
5691 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5692 use_current_size_for_scrollbar_y = true;
5693 }
5694 if (!window->Collapsed)
5695 MarkIniSettingsDirty(window);
5696 }
5697
5698 // Apply minimum/maximum window size constraints and final size
5699 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5700 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5701
5702 // Decoration size
5703 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5704
5705 // POSITION
5706
5707 // Popup latch its initial position, will position itself when it appears next frame
5708 if (window_just_activated_by_user)
5709 {
5710 window->AutoPosLastDirection = ImGuiDir_None;
5711 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
5712 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5713 }
5714
5715 // Position child window
5716 if (flags & ImGuiWindowFlags_ChildWindow)
5717 {
5718 IM_ASSERT(parent_window && parent_window->Active);
5719 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5720 parent_window->DC.ChildWindows.push_back(window);
5721 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5722 window->Pos = parent_window->DC.CursorPos;
5723 }
5724
5725 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5726 if (window_pos_with_pivot)
5727 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5728 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5729 window->Pos = FindBestWindowPosForPopup(window);
5730 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5731 window->Pos = FindBestWindowPosForPopup(window);
5732 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5733 window->Pos = FindBestWindowPosForPopup(window);
5734
5735 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
5736 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
5737 ImRect viewport_rect(GetViewportRect());
5738 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5739 ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding);
5740
5741 // Clamp position/size so window stays visible within its viewport or monitor
5742 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5743 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5744 if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
5745 ClampWindowRect(window, visibility_rect);
5746 window->Pos = ImFloor(window->Pos);
5747
5748 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5749 // Large values tend to lead to variety of artifacts and are not recommended.
5750 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5751
5752 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
5753 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5754 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
5755
5756 // Apply window focus (new and reactivated windows are moved to front)
5757 bool want_focus = false;
5758 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5759 {
5760 if (flags & ImGuiWindowFlags_Popup)
5761 want_focus = true;
5762 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5763 want_focus = true;
5764 }
5765
5766 // Handle manual resize: Resize Grips, Borders, Gamepad
5767 int border_held = -1;
5768 ImU32 resize_grip_col[4] = {};
5769 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5770 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5771 if (!window->Collapsed)
5772 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
5773 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5774 window->ResizeBorderHeld = (signed char)border_held;
5775
5776 // SCROLLBAR VISIBILITY
5777
5778 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5779 if (!window->Collapsed)
5780 {
5781 // When reading the current size we need to read it after size constraints have been applied.
5782 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5783 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5784 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5785 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5786 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5787 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5788 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5789 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5790 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));
5791 if (window->ScrollbarX && !window->ScrollbarY)
5792 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5793 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5794 }
5795
5796 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5797 // Update various regions. Variables they depends on should be set above in this function.
5798 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5799
5800 // Outer rectangle
5801 // Not affected by window border size. Used by:
5802 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5803 // - Begin() initial clipping rect for drawing window background and borders.
5804 // - Begin() clipping whole child
5805 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5806 const ImRect outer_rect = window->Rect();
5807 const ImRect title_bar_rect = window->TitleBarRect();
5808 window->OuterRectClipped = outer_rect;
5809 window->OuterRectClipped.ClipWith(host_rect);
5810
5811 // Inner rectangle
5812 // Not affected by window border size. Used by:
5813 // - InnerClipRect
5814 // - ScrollToBringRectIntoView()
5815 // - NavUpdatePageUpPageDown()
5816 // - Scrollbar()
5817 window->InnerRect.Min.x = window->Pos.x;
5818 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5819 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5820 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5821
5822 // Inner clipping rectangle.
5823 // Will extend a little bit outside the normal work region.
5824 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5825 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5826 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5827 // Affected by window/frame border size. Used by:
5828 // - Begin() initial clip rect
5829 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5830 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5831 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5832 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5833 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5834 window->InnerClipRect.ClipWithFull(host_rect);
5835
5836 // Default item width. Make it proportional to window size if window manually resizes
5837 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5838 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5839 else
5840 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5841
5842 // SCROLLING
5843
5844 // Lock down maximum scrolling
5845 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5846 // for right/bottom aligned items without creating a scrollbar.
5847 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5848 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5849
5850 // Apply scrolling
5851 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
5852 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5853
5854 // DRAWING
5855
5856 // Setup draw list and outer clipping rectangle
5857 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
5858 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5859 PushClipRect(host_rect.Min, host_rect.Max, false);
5860
5861 // Draw modal window background (darkens what is behind them, all viewports)
5862 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5863 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5864 if (dim_bg_for_modal || dim_bg_for_window_list)
5865 {
5866 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5867 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5868 }
5869
5870 // Draw navigation selection/windowing rectangle background
5871 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5872 {
5873 ImRect bb = window->Rect();
5874 bb.Expand(g.FontSize);
5875 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5876 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5877 }
5878
5879 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5880 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5881 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5882 // We also disabled this when we have dimming overlay behind this specific one child.
5883 // 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.
5884 {
5885 bool render_decorations_in_parent = false;
5886 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5887 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5888 render_decorations_in_parent = true;
5889 if (render_decorations_in_parent)
5890 window->DrawList = parent_window->DrawList;
5891
5892 // Handle title bar, scrollbar, resize grips and resize borders
5893 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5894 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5895 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5896
5897 if (render_decorations_in_parent)
5898 window->DrawList = &window->DrawListInst;
5899 }
5900
5901 // Draw navigation selection/windowing rectangle border
5902 if (g.NavWindowingTargetAnim == window)
5903 {
5904 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5905 ImRect bb = window->Rect();
5906 bb.Expand(g.FontSize);
5907 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5908 {
5909 bb.Expand(-g.FontSize - 1.0f);
5910 rounding = window->WindowRounding;
5911 }
5912 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5913 }
5914
5915 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5916
5917 // Work rectangle.
5918 // Affected by window padding and border size. Used by:
5919 // - Columns() for right-most edge
5920 // - TreeNode(), CollapsingHeader() for right-most edge
5921 // - BeginTabBar() for right-most edge
5922 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5923 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5924 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));
5925 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));
5926 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5927 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5928 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5929 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5930 window->ParentWorkRect = window->WorkRect;
5931
5932 // [LEGACY] Content Region
5933 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5934 // Used by:
5935 // - Mouse wheel scrolling + many other things
5936 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5937 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5938 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));
5939 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));
5940
5941 // Setup drawing context
5942 // (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.)
5943 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5944 window->DC.GroupOffset.x = 0.0f;
5945 window->DC.ColumnsOffset.x = 0.0f;
5946 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5947 window->DC.CursorPos = window->DC.CursorStartPos;
5948 window->DC.CursorPosPrevLine = window->DC.CursorPos;
5949 window->DC.CursorMaxPos = window->DC.CursorStartPos;
5950 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5951 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5952
5953 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5954 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5955 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5956 window->DC.NavLayerActiveMaskNext = 0x00;
5957 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // -V595
5958 window->DC.NavHideHighlightOneFrame = false;
5959 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5960
5961 window->DC.MenuBarAppending = false;
5962 window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5963 window->DC.TreeDepth = 0;
5964 window->DC.TreeJumpToParentOnPopMask = 0x00;
5965 window->DC.ChildWindows.resize(0);
5966 window->DC.StateStorage = &window->StateStorage;
5967 window->DC.CurrentColumns = NULL;
5968 window->DC.LayoutType = ImGuiLayoutType_Vertical;
5969 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5970 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
5971
5972 window->DC.ItemWidth = window->ItemWidthDefault;
5973 window->DC.TextWrapPos = -1.0f; // disabled
5974 window->DC.ItemFlagsStack.resize(0);
5975 window->DC.ItemWidthStack.resize(0);
5976 window->DC.TextWrapPosStack.resize(0);
5977 window->DC.GroupStack.resize(0);
5978 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5979 if (parent_window)
5980 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5981
5982 if (window->AutoFitFramesX > 0)
5983 window->AutoFitFramesX--;
5984 if (window->AutoFitFramesY > 0)
5985 window->AutoFitFramesY--;
5986
5987 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5988 if (want_focus)
5989 {
5990 FocusWindow(window);
5991 NavInitWindow(window, false);
5992 }
5993
5994 // Title bar
5995 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5996 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5997
5998 // Clear hit test shape every frame
5999 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
6000
6001 // Pressing CTRL+C while holding on a window copy its content to the clipboard
6002 // 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.
6003 // Maybe we can support CTRL+C on every element?
6004 /*
6005 if (g.ActiveId == move_id)
6006 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
6007 LogToClipboard();
6008 */
6009
6010 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
6011 // This is useful to allow creating context menus on title bar only, etc.
6012 SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);
6013
6014 #ifdef IMGUI_ENABLE_TEST_ENGINE
6015 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
6016 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
6017 #endif
6018 }
6019 else
6020 {
6021 // Append
6022 SetCurrentWindow(window);
6023 }
6024
6025 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
6026
6027 // 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)
6028 if (first_begin_of_the_frame)
6029 window->WriteAccessed = false;
6030
6031 window->BeginCount++;
6032 g.NextWindowData.ClearFlags();
6033
6034 // Update visibility
6035 if (first_begin_of_the_frame)
6036 {
6037 if (flags & ImGuiWindowFlags_ChildWindow)
6038 {
6039 // Child window can be out of sight and have "negative" clip windows.
6040 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
6041 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
6042 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6043 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
6044 window->HiddenFramesCanSkipItems = 1;
6045
6046 // Hide along with parent or if parent is collapsed
6047 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
6048 window->HiddenFramesCanSkipItems = 1;
6049 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
6050 window->HiddenFramesCannotSkipItems = 1;
6051 }
6052
6053 // 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)
6054 if (style.Alpha <= 0.0f)
6055 window->HiddenFramesCanSkipItems = 1;
6056
6057 // Update the Hidden flag
6058 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
6059
6060 // Update the SkipItems flag, used to early out of all items functions (no layout required)
6061 bool skip_items = false;
6062 if (window->Collapsed || !window->Active || window->Hidden)
6063 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
6064 skip_items = true;
6065 window->SkipItems = skip_items;
6066 }
6067
6068 return !window->SkipItems;
6069 }
6070
End()6071 void ImGui::End()
6072 {
6073 ImGuiContext& g = *GImGui;
6074 ImGuiWindow* window = g.CurrentWindow;
6075
6076 // Error checking: verify that user hasn't called End() too many times!
6077 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
6078 {
6079 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
6080 return;
6081 }
6082 IM_ASSERT(g.CurrentWindowStack.Size > 0);
6083
6084 // Error checking: verify that user doesn't directly call End() on a child window.
6085 if (window->Flags & ImGuiWindowFlags_ChildWindow)
6086 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
6087
6088 // Close anything that is open
6089 if (window->DC.CurrentColumns)
6090 EndColumns();
6091 PopClipRect(); // Inner window clip rectangle
6092
6093 // Stop logging
6094 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
6095 LogFinish();
6096
6097 // Pop from window stack
6098 g.CurrentWindowStack.pop_back();
6099 if (window->Flags & ImGuiWindowFlags_Popup)
6100 g.BeginPopupStack.pop_back();
6101 ErrorCheckBeginEndCompareStacksSize(window, false);
6102 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
6103 }
6104
BringWindowToFocusFront(ImGuiWindow * window)6105 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
6106 {
6107 ImGuiContext& g = *GImGui;
6108 if (g.WindowsFocusOrder.back() == window)
6109 return;
6110 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
6111 if (g.WindowsFocusOrder[i] == window)
6112 {
6113 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
6114 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
6115 break;
6116 }
6117 }
6118
BringWindowToDisplayFront(ImGuiWindow * window)6119 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
6120 {
6121 ImGuiContext& g = *GImGui;
6122 ImGuiWindow* current_front_window = g.Windows.back();
6123 if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
6124 return;
6125 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
6126 if (g.Windows[i] == window)
6127 {
6128 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
6129 g.Windows[g.Windows.Size - 1] = window;
6130 break;
6131 }
6132 }
6133
BringWindowToDisplayBack(ImGuiWindow * window)6134 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6135 {
6136 ImGuiContext& g = *GImGui;
6137 if (g.Windows[0] == window)
6138 return;
6139 for (int i = 0; i < g.Windows.Size; i++)
6140 if (g.Windows[i] == window)
6141 {
6142 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6143 g.Windows[0] = window;
6144 break;
6145 }
6146 }
6147
6148 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6149 void ImGui::FocusWindow(ImGuiWindow* window)
6150 {
6151 ImGuiContext& g = *GImGui;
6152
6153 if (g.NavWindow != window)
6154 {
6155 g.NavWindow = window;
6156 if (window && g.NavDisableMouseHover)
6157 g.NavMousePosDirty = true;
6158 g.NavInitRequest = false;
6159 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6160 g.NavFocusScopeId = 0;
6161 g.NavIdIsAlive = false;
6162 g.NavLayer = ImGuiNavLayer_Main;
6163 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6164 }
6165
6166 // Close popups if any
6167 ClosePopupsOverWindow(window, false);
6168
6169 // Move the root window to the top of the pile
6170 IM_ASSERT(window == NULL || window->RootWindow != NULL);
6171 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
6172 ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
6173
6174 // Steal active widgets. Some of the cases it triggers includes:
6175 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
6176 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
6177 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6178 if (!g.ActiveIdNoClearOnFocusLoss)
6179 ClearActiveID();
6180
6181 // Passing NULL allow to disable keyboard focus
6182 if (!window)
6183 return;
6184
6185 // Bring to front
6186 BringWindowToFocusFront(focus_front_window);
6187 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6188 BringWindowToDisplayFront(display_front_window);
6189 }
6190
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6191 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6192 {
6193 ImGuiContext& g = *GImGui;
6194
6195 int start_idx = g.WindowsFocusOrder.Size - 1;
6196 if (under_this_window != NULL)
6197 {
6198 int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6199 if (under_this_window_idx != -1)
6200 start_idx = under_this_window_idx - 1;
6201 }
6202 for (int i = start_idx; i >= 0; i--)
6203 {
6204 // 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.
6205 ImGuiWindow* window = g.WindowsFocusOrder[i];
6206 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
6207 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6208 {
6209 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6210 FocusWindow(focus_window);
6211 return;
6212 }
6213 }
6214 FocusWindow(NULL);
6215 }
6216
SetCurrentFont(ImFont * font)6217 void ImGui::SetCurrentFont(ImFont* font)
6218 {
6219 ImGuiContext& g = *GImGui;
6220 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6221 IM_ASSERT(font->Scale > 0.0f);
6222 g.Font = font;
6223 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6224 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6225
6226 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6227 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6228 g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
6229 g.DrawListSharedData.Font = g.Font;
6230 g.DrawListSharedData.FontSize = g.FontSize;
6231 }
6232
PushFont(ImFont * font)6233 void ImGui::PushFont(ImFont* font)
6234 {
6235 ImGuiContext& g = *GImGui;
6236 if (!font)
6237 font = GetDefaultFont();
6238 SetCurrentFont(font);
6239 g.FontStack.push_back(font);
6240 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6241 }
6242
PopFont()6243 void ImGui::PopFont()
6244 {
6245 ImGuiContext& g = *GImGui;
6246 g.CurrentWindow->DrawList->PopTextureID();
6247 g.FontStack.pop_back();
6248 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6249 }
6250
PushItemFlag(ImGuiItemFlags option,bool enabled)6251 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6252 {
6253 ImGuiWindow* window = GetCurrentWindow();
6254 if (enabled)
6255 window->DC.ItemFlags |= option;
6256 else
6257 window->DC.ItemFlags &= ~option;
6258 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6259 }
6260
PopItemFlag()6261 void ImGui::PopItemFlag()
6262 {
6263 ImGuiWindow* window = GetCurrentWindow();
6264 window->DC.ItemFlagsStack.pop_back();
6265 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6266 }
6267
6268 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6269 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6270 {
6271 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6272 }
6273
PopAllowKeyboardFocus()6274 void ImGui::PopAllowKeyboardFocus()
6275 {
6276 PopItemFlag();
6277 }
6278
PushButtonRepeat(bool repeat)6279 void ImGui::PushButtonRepeat(bool repeat)
6280 {
6281 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6282 }
6283
PopButtonRepeat()6284 void ImGui::PopButtonRepeat()
6285 {
6286 PopItemFlag();
6287 }
6288
PushTextWrapPos(float wrap_pos_x)6289 void ImGui::PushTextWrapPos(float wrap_pos_x)
6290 {
6291 ImGuiWindow* window = GetCurrentWindow();
6292 window->DC.TextWrapPos = wrap_pos_x;
6293 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6294 }
6295
PopTextWrapPos()6296 void ImGui::PopTextWrapPos()
6297 {
6298 ImGuiWindow* window = GetCurrentWindow();
6299 window->DC.TextWrapPosStack.pop_back();
6300 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6301 }
6302
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6303 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6304 {
6305 if (window->RootWindow == potential_parent)
6306 return true;
6307 while (window != NULL)
6308 {
6309 if (window == potential_parent)
6310 return true;
6311 window = window->ParentWindow;
6312 }
6313 return false;
6314 }
6315
IsWindowHovered(ImGuiHoveredFlags flags)6316 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6317 {
6318 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6319 ImGuiContext& g = *GImGui;
6320
6321 if (flags & ImGuiHoveredFlags_AnyWindow)
6322 {
6323 if (g.HoveredWindow == NULL)
6324 return false;
6325 }
6326 else
6327 {
6328 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6329 {
6330 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6331 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6332 return false;
6333 break;
6334 case ImGuiHoveredFlags_RootWindow:
6335 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6336 return false;
6337 break;
6338 case ImGuiHoveredFlags_ChildWindows:
6339 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6340 return false;
6341 break;
6342 default:
6343 if (g.HoveredWindow != g.CurrentWindow)
6344 return false;
6345 break;
6346 }
6347 }
6348
6349 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6350 return false;
6351 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6352 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6353 return false;
6354 return true;
6355 }
6356
IsWindowFocused(ImGuiFocusedFlags flags)6357 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6358 {
6359 ImGuiContext& g = *GImGui;
6360
6361 if (flags & ImGuiFocusedFlags_AnyWindow)
6362 return g.NavWindow != NULL;
6363
6364 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6365 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6366 {
6367 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6368 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6369 case ImGuiFocusedFlags_RootWindow:
6370 return g.NavWindow == g.CurrentWindow->RootWindow;
6371 case ImGuiFocusedFlags_ChildWindows:
6372 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6373 default:
6374 return g.NavWindow == g.CurrentWindow;
6375 }
6376 }
6377
6378 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6379 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
6380 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6381 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6382 {
6383 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6384 }
6385
GetWindowWidth()6386 float ImGui::GetWindowWidth()
6387 {
6388 ImGuiWindow* window = GImGui->CurrentWindow;
6389 return window->Size.x;
6390 }
6391
GetWindowHeight()6392 float ImGui::GetWindowHeight()
6393 {
6394 ImGuiWindow* window = GImGui->CurrentWindow;
6395 return window->Size.y;
6396 }
6397
GetWindowPos()6398 ImVec2 ImGui::GetWindowPos()
6399 {
6400 ImGuiContext& g = *GImGui;
6401 ImGuiWindow* window = g.CurrentWindow;
6402 return window->Pos;
6403 }
6404
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6405 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6406 {
6407 // Test condition (NB: bit 0 is always true) and clear flags for next time
6408 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6409 return;
6410
6411 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6412 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6413 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6414
6415 // Set
6416 const ImVec2 old_pos = window->Pos;
6417 window->Pos = ImFloor(pos);
6418 ImVec2 offset = window->Pos - old_pos;
6419 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
6420 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6421 window->DC.CursorStartPos += offset;
6422 }
6423
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6424 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6425 {
6426 ImGuiWindow* window = GetCurrentWindowRead();
6427 SetWindowPos(window, pos, cond);
6428 }
6429
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6430 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6431 {
6432 if (ImGuiWindow* window = FindWindowByName(name))
6433 SetWindowPos(window, pos, cond);
6434 }
6435
GetWindowSize()6436 ImVec2 ImGui::GetWindowSize()
6437 {
6438 ImGuiWindow* window = GetCurrentWindowRead();
6439 return window->Size;
6440 }
6441
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6442 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6443 {
6444 // Test condition (NB: bit 0 is always true) and clear flags for next time
6445 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6446 return;
6447
6448 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6449 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6450
6451 // Set
6452 if (size.x > 0.0f)
6453 {
6454 window->AutoFitFramesX = 0;
6455 window->SizeFull.x = IM_FLOOR(size.x);
6456 }
6457 else
6458 {
6459 window->AutoFitFramesX = 2;
6460 window->AutoFitOnlyGrows = false;
6461 }
6462 if (size.y > 0.0f)
6463 {
6464 window->AutoFitFramesY = 0;
6465 window->SizeFull.y = IM_FLOOR(size.y);
6466 }
6467 else
6468 {
6469 window->AutoFitFramesY = 2;
6470 window->AutoFitOnlyGrows = false;
6471 }
6472 }
6473
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6474 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6475 {
6476 SetWindowSize(GImGui->CurrentWindow, size, cond);
6477 }
6478
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6479 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6480 {
6481 if (ImGuiWindow* window = FindWindowByName(name))
6482 SetWindowSize(window, size, cond);
6483 }
6484
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6485 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6486 {
6487 // Test condition (NB: bit 0 is always true) and clear flags for next time
6488 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6489 return;
6490 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6491
6492 // Set
6493 window->Collapsed = collapsed;
6494 }
6495
SetWindowHitTestHole(ImGuiWindow * window,const ImVec2 & pos,const ImVec2 & size)6496 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
6497 {
6498 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters
6499 window->HitTestHoleSize = ImVec2ih(size);
6500 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
6501 }
6502
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6503 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6504 {
6505 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6506 }
6507
IsWindowCollapsed()6508 bool ImGui::IsWindowCollapsed()
6509 {
6510 ImGuiWindow* window = GetCurrentWindowRead();
6511 return window->Collapsed;
6512 }
6513
IsWindowAppearing()6514 bool ImGui::IsWindowAppearing()
6515 {
6516 ImGuiWindow* window = GetCurrentWindowRead();
6517 return window->Appearing;
6518 }
6519
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6520 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6521 {
6522 if (ImGuiWindow* window = FindWindowByName(name))
6523 SetWindowCollapsed(window, collapsed, cond);
6524 }
6525
SetWindowFocus()6526 void ImGui::SetWindowFocus()
6527 {
6528 FocusWindow(GImGui->CurrentWindow);
6529 }
6530
SetWindowFocus(const char * name)6531 void ImGui::SetWindowFocus(const char* name)
6532 {
6533 if (name)
6534 {
6535 if (ImGuiWindow* window = FindWindowByName(name))
6536 FocusWindow(window);
6537 }
6538 else
6539 {
6540 FocusWindow(NULL);
6541 }
6542 }
6543
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6544 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6545 {
6546 ImGuiContext& g = *GImGui;
6547 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6548 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6549 g.NextWindowData.PosVal = pos;
6550 g.NextWindowData.PosPivotVal = pivot;
6551 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6552 }
6553
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6554 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6555 {
6556 ImGuiContext& g = *GImGui;
6557 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6558 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6559 g.NextWindowData.SizeVal = size;
6560 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6561 }
6562
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6563 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6564 {
6565 ImGuiContext& g = *GImGui;
6566 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6567 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6568 g.NextWindowData.SizeCallback = custom_callback;
6569 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6570 }
6571
6572 // Content size = inner scrollable rectangle, padded with WindowPadding.
6573 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6574 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6575 {
6576 ImGuiContext& g = *GImGui;
6577 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6578 g.NextWindowData.ContentSizeVal = size;
6579 }
6580
SetNextWindowScroll(const ImVec2 & scroll)6581 void ImGui::SetNextWindowScroll(const ImVec2& scroll)
6582 {
6583 ImGuiContext& g = *GImGui;
6584 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;
6585 g.NextWindowData.ScrollVal = scroll;
6586 }
6587
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6588 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6589 {
6590 ImGuiContext& g = *GImGui;
6591 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6592 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6593 g.NextWindowData.CollapsedVal = collapsed;
6594 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6595 }
6596
SetNextWindowFocus()6597 void ImGui::SetNextWindowFocus()
6598 {
6599 ImGuiContext& g = *GImGui;
6600 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6601 }
6602
SetNextWindowBgAlpha(float alpha)6603 void ImGui::SetNextWindowBgAlpha(float alpha)
6604 {
6605 ImGuiContext& g = *GImGui;
6606 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6607 g.NextWindowData.BgAlphaVal = alpha;
6608 }
6609
GetWindowDrawList()6610 ImDrawList* ImGui::GetWindowDrawList()
6611 {
6612 ImGuiWindow* window = GetCurrentWindow();
6613 return window->DrawList;
6614 }
6615
GetFont()6616 ImFont* ImGui::GetFont()
6617 {
6618 return GImGui->Font;
6619 }
6620
GetFontSize()6621 float ImGui::GetFontSize()
6622 {
6623 return GImGui->FontSize;
6624 }
6625
GetFontTexUvWhitePixel()6626 ImVec2 ImGui::GetFontTexUvWhitePixel()
6627 {
6628 return GImGui->DrawListSharedData.TexUvWhitePixel;
6629 }
6630
SetWindowFontScale(float scale)6631 void ImGui::SetWindowFontScale(float scale)
6632 {
6633 IM_ASSERT(scale > 0.0f);
6634 ImGuiContext& g = *GImGui;
6635 ImGuiWindow* window = GetCurrentWindow();
6636 window->FontWindowScale = scale;
6637 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6638 }
6639
ActivateItem(ImGuiID id)6640 void ImGui::ActivateItem(ImGuiID id)
6641 {
6642 ImGuiContext& g = *GImGui;
6643 g.NavNextActivateId = id;
6644 }
6645
6646 // Note: this is storing in same stack as IDStack, so Push/Pop mismatch will be reported there.
PushFocusScope(ImGuiID id)6647 void ImGui::PushFocusScope(ImGuiID id)
6648 {
6649 ImGuiContext& g = *GImGui;
6650 ImGuiWindow* window = g.CurrentWindow;
6651 window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent);
6652 window->DC.NavFocusScopeIdCurrent = id;
6653 }
6654
PopFocusScope()6655 void ImGui::PopFocusScope()
6656 {
6657 ImGuiContext& g = *GImGui;
6658 ImGuiWindow* window = g.CurrentWindow;
6659 window->DC.NavFocusScopeIdCurrent = window->IDStack.back();
6660 window->IDStack.pop_back();
6661 }
6662
SetKeyboardFocusHere(int offset)6663 void ImGui::SetKeyboardFocusHere(int offset)
6664 {
6665 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6666 ImGuiContext& g = *GImGui;
6667 ImGuiWindow* window = g.CurrentWindow;
6668 g.FocusRequestNextWindow = window;
6669 g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
6670 g.FocusRequestNextCounterTabStop = INT_MAX;
6671 }
6672
SetItemDefaultFocus()6673 void ImGui::SetItemDefaultFocus()
6674 {
6675 ImGuiContext& g = *GImGui;
6676 ImGuiWindow* window = g.CurrentWindow;
6677 if (!window->Appearing)
6678 return;
6679 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6680 {
6681 g.NavInitRequest = false;
6682 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6683 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6684 NavUpdateAnyRequestFlag();
6685 if (!IsItemVisible())
6686 SetScrollHereY();
6687 }
6688 }
6689
SetStateStorage(ImGuiStorage * tree)6690 void ImGui::SetStateStorage(ImGuiStorage* tree)
6691 {
6692 ImGuiWindow* window = GImGui->CurrentWindow;
6693 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6694 }
6695
GetStateStorage()6696 ImGuiStorage* ImGui::GetStateStorage()
6697 {
6698 ImGuiWindow* window = GImGui->CurrentWindow;
6699 return window->DC.StateStorage;
6700 }
6701
PushID(const char * str_id)6702 void ImGui::PushID(const char* str_id)
6703 {
6704 ImGuiContext& g = *GImGui;
6705 ImGuiWindow* window = g.CurrentWindow;
6706 ImGuiID id = window->GetIDNoKeepAlive(str_id);
6707 window->IDStack.push_back(id);
6708 }
6709
PushID(const char * str_id_begin,const char * str_id_end)6710 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6711 {
6712 ImGuiContext& g = *GImGui;
6713 ImGuiWindow* window = g.CurrentWindow;
6714 ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end);
6715 window->IDStack.push_back(id);
6716 }
6717
PushID(const void * ptr_id)6718 void ImGui::PushID(const void* ptr_id)
6719 {
6720 ImGuiContext& g = *GImGui;
6721 ImGuiWindow* window = g.CurrentWindow;
6722 ImGuiID id = window->GetIDNoKeepAlive(ptr_id);
6723 window->IDStack.push_back(id);
6724 }
6725
PushID(int int_id)6726 void ImGui::PushID(int int_id)
6727 {
6728 ImGuiContext& g = *GImGui;
6729 ImGuiWindow* window = g.CurrentWindow;
6730 ImGuiID id = window->GetIDNoKeepAlive(int_id);
6731 window->IDStack.push_back(id);
6732 }
6733
6734 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6735 void ImGui::PushOverrideID(ImGuiID id)
6736 {
6737 ImGuiContext& g = *GImGui;
6738 ImGuiWindow* window = g.CurrentWindow;
6739 window->IDStack.push_back(id);
6740 }
6741
PopID()6742 void ImGui::PopID()
6743 {
6744 ImGuiWindow* window = GImGui->CurrentWindow;
6745 window->IDStack.pop_back();
6746 }
6747
GetID(const char * str_id)6748 ImGuiID ImGui::GetID(const char* str_id)
6749 {
6750 ImGuiWindow* window = GImGui->CurrentWindow;
6751 return window->GetID(str_id);
6752 }
6753
GetID(const char * str_id_begin,const char * str_id_end)6754 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6755 {
6756 ImGuiWindow* window = GImGui->CurrentWindow;
6757 return window->GetID(str_id_begin, str_id_end);
6758 }
6759
GetID(const void * ptr_id)6760 ImGuiID ImGui::GetID(const void* ptr_id)
6761 {
6762 ImGuiWindow* window = GImGui->CurrentWindow;
6763 return window->GetID(ptr_id);
6764 }
6765
IsRectVisible(const ImVec2 & size)6766 bool ImGui::IsRectVisible(const ImVec2& size)
6767 {
6768 ImGuiWindow* window = GImGui->CurrentWindow;
6769 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6770 }
6771
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6772 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6773 {
6774 ImGuiWindow* window = GImGui->CurrentWindow;
6775 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6776 }
6777
6778
6779 //-----------------------------------------------------------------------------
6780 // [SECTION] ERROR CHECKING
6781 //-----------------------------------------------------------------------------
6782
6783 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
6784 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
6785 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
6786 // may see different structures than what imgui.cpp sees, which is problematic.
6787 // We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert,size_t sz_idx)6788 bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
6789 {
6790 bool error = false;
6791 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
6792 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
6793 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
6794 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
6795 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
6796 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
6797 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
6798 return !error;
6799 }
6800
ErrorCheckNewFrameSanityChecks()6801 static void ImGui::ErrorCheckNewFrameSanityChecks()
6802 {
6803 ImGuiContext& g = *GImGui;
6804
6805 // Check user IM_ASSERT macro
6806 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
6807 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
6808 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
6809 // #define IM_ASSERT(EXPR) SomeCode(EXPR); SomeMoreCode(); // Wrong!
6810 // #define IM_ASSERT(EXPR) do { SomeCode(EXPR); SomeMoreCode(); } while (0) // Correct!
6811 if (true) IM_ASSERT(1); else IM_ASSERT(0);
6812
6813 // Check user data
6814 // (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)
6815 IM_ASSERT(g.Initialized);
6816 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
6817 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
6818 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
6819 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
6820 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
6821 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
6822 IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!");
6823 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!");
6824 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
6825 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
6826 for (int n = 0; n < ImGuiKey_COUNT; n++)
6827 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)");
6828
6829 // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
6830 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
6831 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
6832
6833 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
6834 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
6835 g.IO.ConfigWindowsResizeFromEdges = false;
6836 }
6837
ErrorCheckEndFrameSanityChecks()6838 static void ImGui::ErrorCheckEndFrameSanityChecks()
6839 {
6840 ImGuiContext& g = *GImGui;
6841
6842 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
6843 // One possible reason leading to this assert is that your back-ends update inputs _AFTER_ NewFrame().
6844 const ImGuiKeyModFlags expected_key_mod_flags = GetMergedKeyModFlags();
6845 IM_ASSERT(g.IO.KeyMods == expected_key_mod_flags && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
6846 IM_UNUSED(expected_key_mod_flags);
6847
6848 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
6849 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
6850 if (g.CurrentWindowStack.Size != 1)
6851 {
6852 if (g.CurrentWindowStack.Size > 1)
6853 {
6854 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
6855 while (g.CurrentWindowStack.Size > 1)
6856 End();
6857 }
6858 else
6859 {
6860 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
6861 }
6862 }
6863 }
6864
6865 // Save and compare stack sizes on Begin()/End() to detect usage errors
6866 // Begin() calls this with write=true
6867 // End() calls this with write=false
ErrorCheckBeginEndCompareStacksSize(ImGuiWindow * window,bool write)6868 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write)
6869 {
6870 ImGuiContext& g = *GImGui;
6871 short* p = &window->DC.StackSizesBackup[0];
6872
6873 // Window stacks
6874 // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)
6875 { int n = window->IDStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!"); p++; } // Too few or too many PopID()/TreePop()
6876 { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!"); p++; } // Too few or too many EndGroup()
6877
6878 // Global stacks
6879 // 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.
6880 { int n = g.BeginPopupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup()
6881 { int n = g.ColorModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!"); p++; } // Too few or too many PopStyleColor()
6882 { int n = g.StyleModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!"); p++; } // Too few or too many PopStyleVar()
6883 { int n = g.FontStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!"); p++; } // Too few or too many PopFont()
6884 IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
6885 }
6886
6887
6888 //-----------------------------------------------------------------------------
6889 // [SECTION] LAYOUT
6890 //-----------------------------------------------------------------------------
6891 // - ItemSize()
6892 // - ItemAdd()
6893 // - SameLine()
6894 // - GetCursorScreenPos()
6895 // - SetCursorScreenPos()
6896 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
6897 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
6898 // - GetCursorStartPos()
6899 // - Indent()
6900 // - Unindent()
6901 // - SetNextItemWidth()
6902 // - PushItemWidth()
6903 // - PushMultiItemsWidths()
6904 // - PopItemWidth()
6905 // - CalcItemWidth()
6906 // - CalcItemSize()
6907 // - GetTextLineHeight()
6908 // - GetTextLineHeightWithSpacing()
6909 // - GetFrameHeight()
6910 // - GetFrameHeightWithSpacing()
6911 // - GetContentRegionMax()
6912 // - GetContentRegionMaxAbs() [Internal]
6913 // - GetContentRegionAvail(),
6914 // - GetWindowContentRegionMin(), GetWindowContentRegionMax()
6915 // - GetWindowContentRegionWidth()
6916 // - BeginGroup()
6917 // - EndGroup()
6918 // Also see in imgui_widgets: tab bars, columns.
6919 //-----------------------------------------------------------------------------
6920
6921 // Advance cursor given item size for layout.
6922 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
6923 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
ItemSize(const ImVec2 & size,float text_baseline_y)6924 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
6925 {
6926 ImGuiContext& g = *GImGui;
6927 ImGuiWindow* window = g.CurrentWindow;
6928 if (window->SkipItems)
6929 return;
6930
6931 // We increase the height in this function to accommodate for baseline offset.
6932 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
6933 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
6934 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
6935 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
6936
6937 // Always align ourselves on pixel boundaries
6938 //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]
6939 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
6940 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
6941 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
6942 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
6943 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
6944 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
6945 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
6946
6947 window->DC.PrevLineSize.y = line_height;
6948 window->DC.CurrLineSize.y = 0.0f;
6949 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
6950 window->DC.CurrLineTextBaseOffset = 0.0f;
6951
6952 // Horizontal layout mode
6953 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
6954 SameLine();
6955 }
6956
ItemSize(const ImRect & bb,float text_baseline_y)6957 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
6958 {
6959 ItemSize(bb.GetSize(), text_baseline_y);
6960 }
6961
6962 // Declare item bounding box for clipping and interaction.
6963 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
6964 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)6965 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
6966 {
6967 ImGuiContext& g = *GImGui;
6968 ImGuiWindow* window = g.CurrentWindow;
6969
6970 if (id != 0)
6971 {
6972 // Navigation processing runs prior to clipping early-out
6973 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
6974 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
6975 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
6976 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
6977 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
6978 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
6979 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
6980 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
6981 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
6982 if (g.NavId == id || g.NavAnyRequest)
6983 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
6984 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
6985 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
6986
6987 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
6988 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
6989 if (id == g.DebugItemPickerBreakId)
6990 {
6991 IM_DEBUG_BREAK();
6992 g.DebugItemPickerBreakId = 0;
6993 }
6994 #endif
6995 }
6996
6997 // Equivalent to calling SetLastItemData()
6998 window->DC.LastItemId = id;
6999 window->DC.LastItemRect = bb;
7000 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
7001 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
7002
7003 #ifdef IMGUI_ENABLE_TEST_ENGINE
7004 if (id != 0)
7005 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
7006 #endif
7007
7008 // Clipping test
7009 const bool is_clipped = IsClippedEx(bb, id, false);
7010 if (is_clipped)
7011 return false;
7012 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
7013
7014 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
7015 if (IsMouseHoveringRect(bb.Min, bb.Max))
7016 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
7017 return true;
7018 }
7019
7020 // Gets back to previous line and continue with horizontal layout
7021 // offset_from_start_x == 0 : follow right after previous item
7022 // offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7023 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
7024 // spacing_w >= 0 : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7025 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7026 {
7027 ImGuiWindow* window = GetCurrentWindow();
7028 if (window->SkipItems)
7029 return;
7030
7031 ImGuiContext& g = *GImGui;
7032 if (offset_from_start_x != 0.0f)
7033 {
7034 if (spacing_w < 0.0f) spacing_w = 0.0f;
7035 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7036 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7037 }
7038 else
7039 {
7040 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7041 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7042 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7043 }
7044 window->DC.CurrLineSize = window->DC.PrevLineSize;
7045 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7046 }
7047
GetCursorScreenPos()7048 ImVec2 ImGui::GetCursorScreenPos()
7049 {
7050 ImGuiWindow* window = GetCurrentWindowRead();
7051 return window->DC.CursorPos;
7052 }
7053
SetCursorScreenPos(const ImVec2 & pos)7054 void ImGui::SetCursorScreenPos(const ImVec2& pos)
7055 {
7056 ImGuiWindow* window = GetCurrentWindow();
7057 window->DC.CursorPos = pos;
7058 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7059 }
7060
7061 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
7062 // Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
GetCursorPos()7063 ImVec2 ImGui::GetCursorPos()
7064 {
7065 ImGuiWindow* window = GetCurrentWindowRead();
7066 return window->DC.CursorPos - window->Pos + window->Scroll;
7067 }
7068
GetCursorPosX()7069 float ImGui::GetCursorPosX()
7070 {
7071 ImGuiWindow* window = GetCurrentWindowRead();
7072 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
7073 }
7074
GetCursorPosY()7075 float ImGui::GetCursorPosY()
7076 {
7077 ImGuiWindow* window = GetCurrentWindowRead();
7078 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
7079 }
7080
SetCursorPos(const ImVec2 & local_pos)7081 void ImGui::SetCursorPos(const ImVec2& local_pos)
7082 {
7083 ImGuiWindow* window = GetCurrentWindow();
7084 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
7085 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
7086 }
7087
SetCursorPosX(float x)7088 void ImGui::SetCursorPosX(float x)
7089 {
7090 ImGuiWindow* window = GetCurrentWindow();
7091 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
7092 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
7093 }
7094
SetCursorPosY(float y)7095 void ImGui::SetCursorPosY(float y)
7096 {
7097 ImGuiWindow* window = GetCurrentWindow();
7098 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
7099 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
7100 }
7101
GetCursorStartPos()7102 ImVec2 ImGui::GetCursorStartPos()
7103 {
7104 ImGuiWindow* window = GetCurrentWindowRead();
7105 return window->DC.CursorStartPos - window->Pos;
7106 }
7107
Indent(float indent_w)7108 void ImGui::Indent(float indent_w)
7109 {
7110 ImGuiContext& g = *GImGui;
7111 ImGuiWindow* window = GetCurrentWindow();
7112 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7113 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7114 }
7115
Unindent(float indent_w)7116 void ImGui::Unindent(float indent_w)
7117 {
7118 ImGuiContext& g = *GImGui;
7119 ImGuiWindow* window = GetCurrentWindow();
7120 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7121 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7122 }
7123
7124 // Affect large frame+labels widgets only.
SetNextItemWidth(float item_width)7125 void ImGui::SetNextItemWidth(float item_width)
7126 {
7127 ImGuiContext& g = *GImGui;
7128 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
7129 g.NextItemData.Width = item_width;
7130 }
7131
PushItemWidth(float item_width)7132 void ImGui::PushItemWidth(float item_width)
7133 {
7134 ImGuiContext& g = *GImGui;
7135 ImGuiWindow* window = g.CurrentWindow;
7136 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
7137 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
7138 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7139 }
7140
PushMultiItemsWidths(int components,float w_full)7141 void ImGui::PushMultiItemsWidths(int components, float w_full)
7142 {
7143 ImGuiContext& g = *GImGui;
7144 ImGuiWindow* window = g.CurrentWindow;
7145 const ImGuiStyle& style = g.Style;
7146 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
7147 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
7148 window->DC.ItemWidthStack.push_back(w_item_last);
7149 for (int i = 0; i < components - 1; i++)
7150 window->DC.ItemWidthStack.push_back(w_item_one);
7151 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
7152 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
7153 }
7154
PopItemWidth()7155 void ImGui::PopItemWidth()
7156 {
7157 ImGuiWindow* window = GetCurrentWindow();
7158 window->DC.ItemWidthStack.pop_back();
7159 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
7160 }
7161
7162 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
7163 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()7164 float ImGui::CalcItemWidth()
7165 {
7166 ImGuiContext& g = *GImGui;
7167 ImGuiWindow* window = g.CurrentWindow;
7168 float w;
7169 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
7170 w = g.NextItemData.Width;
7171 else
7172 w = window->DC.ItemWidth;
7173 if (w < 0.0f)
7174 {
7175 float region_max_x = GetContentRegionMaxAbs().x;
7176 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
7177 }
7178 w = IM_FLOOR(w);
7179 return w;
7180 }
7181
7182 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
7183 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
7184 // Note that only CalcItemWidth() is publicly exposed.
7185 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
CalcItemSize(ImVec2 size,float default_w,float default_h)7186 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
7187 {
7188 ImGuiWindow* window = GImGui->CurrentWindow;
7189
7190 ImVec2 region_max;
7191 if (size.x < 0.0f || size.y < 0.0f)
7192 region_max = GetContentRegionMaxAbs();
7193
7194 if (size.x == 0.0f)
7195 size.x = default_w;
7196 else if (size.x < 0.0f)
7197 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
7198
7199 if (size.y == 0.0f)
7200 size.y = default_h;
7201 else if (size.y < 0.0f)
7202 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
7203
7204 return size;
7205 }
7206
GetTextLineHeight()7207 float ImGui::GetTextLineHeight()
7208 {
7209 ImGuiContext& g = *GImGui;
7210 return g.FontSize;
7211 }
7212
GetTextLineHeightWithSpacing()7213 float ImGui::GetTextLineHeightWithSpacing()
7214 {
7215 ImGuiContext& g = *GImGui;
7216 return g.FontSize + g.Style.ItemSpacing.y;
7217 }
7218
GetFrameHeight()7219 float ImGui::GetFrameHeight()
7220 {
7221 ImGuiContext& g = *GImGui;
7222 return g.FontSize + g.Style.FramePadding.y * 2.0f;
7223 }
7224
GetFrameHeightWithSpacing()7225 float ImGui::GetFrameHeightWithSpacing()
7226 {
7227 ImGuiContext& g = *GImGui;
7228 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
7229 }
7230
7231 // 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!
7232
7233 // FIXME: This is in window space (not screen space!).
GetContentRegionMax()7234 ImVec2 ImGui::GetContentRegionMax()
7235 {
7236 ImGuiContext& g = *GImGui;
7237 ImGuiWindow* window = g.CurrentWindow;
7238 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
7239 if (window->DC.CurrentColumns)
7240 mx.x = window->WorkRect.Max.x - window->Pos.x;
7241 return mx;
7242 }
7243
7244 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()7245 ImVec2 ImGui::GetContentRegionMaxAbs()
7246 {
7247 ImGuiContext& g = *GImGui;
7248 ImGuiWindow* window = g.CurrentWindow;
7249 ImVec2 mx = window->ContentRegionRect.Max;
7250 if (window->DC.CurrentColumns)
7251 mx.x = window->WorkRect.Max.x;
7252 return mx;
7253 }
7254
GetContentRegionAvail()7255 ImVec2 ImGui::GetContentRegionAvail()
7256 {
7257 ImGuiWindow* window = GImGui->CurrentWindow;
7258 return GetContentRegionMaxAbs() - window->DC.CursorPos;
7259 }
7260
7261 // In window space (not screen space!)
GetWindowContentRegionMin()7262 ImVec2 ImGui::GetWindowContentRegionMin()
7263 {
7264 ImGuiWindow* window = GImGui->CurrentWindow;
7265 return window->ContentRegionRect.Min - window->Pos;
7266 }
7267
GetWindowContentRegionMax()7268 ImVec2 ImGui::GetWindowContentRegionMax()
7269 {
7270 ImGuiWindow* window = GImGui->CurrentWindow;
7271 return window->ContentRegionRect.Max - window->Pos;
7272 }
7273
GetWindowContentRegionWidth()7274 float ImGui::GetWindowContentRegionWidth()
7275 {
7276 ImGuiWindow* window = GImGui->CurrentWindow;
7277 return window->ContentRegionRect.GetWidth();
7278 }
7279
7280 // 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.)
7281 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
BeginGroup()7282 void ImGui::BeginGroup()
7283 {
7284 ImGuiContext& g = *GImGui;
7285 ImGuiWindow* window = g.CurrentWindow;
7286
7287 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
7288 ImGuiGroupData& group_data = window->DC.GroupStack.back();
7289 group_data.BackupCursorPos = window->DC.CursorPos;
7290 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7291 group_data.BackupIndent = window->DC.Indent;
7292 group_data.BackupGroupOffset = window->DC.GroupOffset;
7293 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7294 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7295 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7296 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7297 group_data.EmitItem = true;
7298
7299 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7300 window->DC.Indent = window->DC.GroupOffset;
7301 window->DC.CursorMaxPos = window->DC.CursorPos;
7302 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7303 if (g.LogEnabled)
7304 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7305 }
7306
EndGroup()7307 void ImGui::EndGroup()
7308 {
7309 ImGuiContext& g = *GImGui;
7310 ImGuiWindow* window = g.CurrentWindow;
7311 IM_ASSERT(window->DC.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
7312
7313 ImGuiGroupData& group_data = window->DC.GroupStack.back();
7314
7315 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7316
7317 window->DC.CursorPos = group_data.BackupCursorPos;
7318 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7319 window->DC.Indent = group_data.BackupIndent;
7320 window->DC.GroupOffset = group_data.BackupGroupOffset;
7321 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7322 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7323 if (g.LogEnabled)
7324 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7325
7326 if (!group_data.EmitItem)
7327 {
7328 window->DC.GroupStack.pop_back();
7329 return;
7330 }
7331
7332 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.
7333 ItemSize(group_bb.GetSize());
7334 ItemAdd(group_bb, 0);
7335
7336 // 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.
7337 // 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.
7338 // Also if you grep for LastItemId you'll notice it is only used in that context.
7339 // (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.)
7340 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7341 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);
7342 if (group_contains_curr_active_id)
7343 window->DC.LastItemId = g.ActiveId;
7344 else if (group_contains_prev_active_id)
7345 window->DC.LastItemId = g.ActiveIdPreviousFrame;
7346 window->DC.LastItemRect = group_bb;
7347
7348 // Forward Edited flag
7349 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7350 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
7351
7352 // Forward Deactivated flag
7353 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7354 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7355 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
7356
7357 window->DC.GroupStack.pop_back();
7358 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
7359 }
7360
7361
7362 //-----------------------------------------------------------------------------
7363 // [SECTION] SCROLLING
7364 //-----------------------------------------------------------------------------
7365
7366 // Helper to snap on edges when aiming at an item very close to the edge,
7367 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
7368 // When we refactor the scrolling API this may be configurable with a flag?
7369 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
CalcScrollEdgeSnap(float target,float snap_min,float snap_max,float snap_threshold,float center_ratio)7370 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
7371 {
7372 if (target <= snap_min + snap_threshold)
7373 return ImLerp(snap_min, target, center_ratio);
7374 if (target >= snap_max - snap_threshold)
7375 return ImLerp(target, snap_max, center_ratio);
7376 return target;
7377 }
7378
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window)7379 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
7380 {
7381 ImVec2 scroll = window->Scroll;
7382 if (window->ScrollTarget.x < FLT_MAX)
7383 {
7384 float center_x_ratio = window->ScrollTargetCenterRatio.x;
7385 float scroll_target_x = window->ScrollTarget.x;
7386 float snap_x_min = 0.0f;
7387 float snap_x_max = window->ScrollMax.x + window->Size.x;
7388 if (window->ScrollTargetEdgeSnapDist.x > 0.0f)
7389 scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio);
7390 scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - window->ScrollbarSizes.x);
7391 }
7392 if (window->ScrollTarget.y < FLT_MAX)
7393 {
7394 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7395 float center_y_ratio = window->ScrollTargetCenterRatio.y;
7396 float scroll_target_y = window->ScrollTarget.y;
7397 float snap_y_min = 0.0f;
7398 float snap_y_max = window->ScrollMax.y + window->Size.y - decoration_up_height;
7399 if (window->ScrollTargetEdgeSnapDist.y > 0.0f)
7400 scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio);
7401 scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7402 }
7403 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f));
7404 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f));
7405 if (!window->Collapsed && !window->SkipItems)
7406 {
7407 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7408 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7409 }
7410 return scroll;
7411 }
7412
7413 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7414 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7415 {
7416 ImGuiContext& g = *GImGui;
7417 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7418 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7419
7420 ImVec2 delta_scroll;
7421 if (!window_rect.Contains(item_rect))
7422 {
7423 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7424 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f);
7425 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7426 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7427 if (item_rect.Min.y < window_rect.Min.y)
7428 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7429 else if (item_rect.Max.y >= window_rect.Max.y)
7430 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7431
7432 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7433 delta_scroll = next_scroll - window->Scroll;
7434 }
7435
7436 // Also scroll parent window to keep us into view if necessary
7437 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7438 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7439
7440 return delta_scroll;
7441 }
7442
GetScrollX()7443 float ImGui::GetScrollX()
7444 {
7445 ImGuiWindow* window = GImGui->CurrentWindow;
7446 return window->Scroll.x;
7447 }
7448
GetScrollY()7449 float ImGui::GetScrollY()
7450 {
7451 ImGuiWindow* window = GImGui->CurrentWindow;
7452 return window->Scroll.y;
7453 }
7454
GetScrollMaxX()7455 float ImGui::GetScrollMaxX()
7456 {
7457 ImGuiWindow* window = GImGui->CurrentWindow;
7458 return window->ScrollMax.x;
7459 }
7460
GetScrollMaxY()7461 float ImGui::GetScrollMaxY()
7462 {
7463 ImGuiWindow* window = GImGui->CurrentWindow;
7464 return window->ScrollMax.y;
7465 }
7466
SetScrollX(ImGuiWindow * window,float scroll_x)7467 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
7468 {
7469 window->ScrollTarget.x = scroll_x;
7470 window->ScrollTargetCenterRatio.x = 0.0f;
7471 window->ScrollTargetEdgeSnapDist.x = 0.0f;
7472 }
7473
SetScrollY(ImGuiWindow * window,float scroll_y)7474 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
7475 {
7476 window->ScrollTarget.y = scroll_y;
7477 window->ScrollTargetCenterRatio.y = 0.0f;
7478 window->ScrollTargetEdgeSnapDist.y = 0.0f;
7479 }
7480
SetScrollX(float scroll_x)7481 void ImGui::SetScrollX(float scroll_x)
7482 {
7483 ImGuiContext& g = *GImGui;
7484 SetScrollX(g.CurrentWindow, scroll_x);
7485 }
7486
SetScrollY(float scroll_y)7487 void ImGui::SetScrollY(float scroll_y)
7488 {
7489 ImGuiContext& g = *GImGui;
7490 SetScrollY(g.CurrentWindow, scroll_y);
7491 }
7492
7493 // Note that a local position will vary depending on initial scroll value,
7494 // This is a little bit confusing so bear with us:
7495 // - local_pos = (absolution_pos - window->Pos)
7496 // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
7497 // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
7498 // - They mostly exists because of legacy API.
7499 // Following the rules above, when trying to work with scrolling code, consider that:
7500 // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
7501 // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
7502 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7503 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7504 {
7505 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7506 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset
7507 window->ScrollTargetCenterRatio.x = center_x_ratio;
7508 window->ScrollTargetEdgeSnapDist.x = 0.0f;
7509 }
7510
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7511 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7512 {
7513 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7514 local_y -= window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect
7515 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset
7516 window->ScrollTargetCenterRatio.y = center_y_ratio;
7517 window->ScrollTargetEdgeSnapDist.y = 0.0f;
7518 }
7519
SetScrollFromPosX(float local_x,float center_x_ratio)7520 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7521 {
7522 ImGuiContext& g = *GImGui;
7523 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7524 }
7525
SetScrollFromPosY(float local_y,float center_y_ratio)7526 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7527 {
7528 ImGuiContext& g = *GImGui;
7529 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7530 }
7531
7532 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
SetScrollHereX(float center_x_ratio)7533 void ImGui::SetScrollHereX(float center_x_ratio)
7534 {
7535 ImGuiContext& g = *GImGui;
7536 ImGuiWindow* window = g.CurrentWindow;
7537 float spacing_x = g.Style.ItemSpacing.x;
7538 float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio);
7539 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
7540
7541 // Tweak: snap on edges when aiming at an item very close to the edge
7542 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
7543 }
7544
7545 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHereY(float center_y_ratio)7546 void ImGui::SetScrollHereY(float center_y_ratio)
7547 {
7548 ImGuiContext& g = *GImGui;
7549 ImGuiWindow* window = g.CurrentWindow;
7550 float spacing_y = g.Style.ItemSpacing.y;
7551 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
7552 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
7553
7554 // Tweak: snap on edges when aiming at an item very close to the edge
7555 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
7556 }
7557
7558 //-----------------------------------------------------------------------------
7559 // [SECTION] TOOLTIPS
7560 //-----------------------------------------------------------------------------
7561
BeginTooltip()7562 void ImGui::BeginTooltip()
7563 {
7564 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
7565 }
7566
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)7567 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
7568 {
7569 ImGuiContext& g = *GImGui;
7570
7571 if (g.DragDropWithinSource || g.DragDropWithinTarget)
7572 {
7573 // 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)
7574 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7575 // 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.
7576 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7577 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7578 SetNextWindowPos(tooltip_pos);
7579 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7580 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7581 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
7582 }
7583
7584 char window_name[16];
7585 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7586 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
7587 if (ImGuiWindow* window = FindWindowByName(window_name))
7588 if (window->Active)
7589 {
7590 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7591 window->Hidden = true;
7592 window->HiddenFramesCanSkipItems = 1;
7593 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7594 }
7595 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
7596 Begin(window_name, NULL, flags | extra_flags);
7597 }
7598
EndTooltip()7599 void ImGui::EndTooltip()
7600 {
7601 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
7602 End();
7603 }
7604
SetTooltipV(const char * fmt,va_list args)7605 void ImGui::SetTooltipV(const char* fmt, va_list args)
7606 {
7607 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
7608 TextV(fmt, args);
7609 EndTooltip();
7610 }
7611
SetTooltip(const char * fmt,...)7612 void ImGui::SetTooltip(const char* fmt, ...)
7613 {
7614 va_list args;
7615 va_start(args, fmt);
7616 SetTooltipV(fmt, args);
7617 va_end(args);
7618 }
7619
7620 //-----------------------------------------------------------------------------
7621 // [SECTION] POPUPS
7622 //-----------------------------------------------------------------------------
7623
7624 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
IsPopupOpen(ImGuiID id,ImGuiPopupFlags popup_flags)7625 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
7626 {
7627 ImGuiContext& g = *GImGui;
7628 if (popup_flags & ImGuiPopupFlags_AnyPopupId)
7629 {
7630 // Return true if any popup is open at the current BeginPopup() level of the popup stack
7631 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
7632 IM_ASSERT(id == 0);
7633 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
7634 return g.OpenPopupStack.Size > 0;
7635 else
7636 return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
7637 }
7638 else
7639 {
7640 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
7641 {
7642 // Return true if the popup is open anywhere in the popup stack
7643 for (int n = 0; n < g.OpenPopupStack.Size; n++)
7644 if (g.OpenPopupStack[n].PopupId == id)
7645 return true;
7646 return false;
7647 }
7648 else
7649 {
7650 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
7651 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7652 }
7653 }
7654 }
7655
IsPopupOpen(const char * str_id,ImGuiPopupFlags popup_flags)7656 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
7657 {
7658 ImGuiContext& g = *GImGui;
7659 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
7660 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
7661 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
7662 return IsPopupOpen(id, popup_flags);
7663 }
7664
GetTopMostPopupModal()7665 ImGuiWindow* ImGui::GetTopMostPopupModal()
7666 {
7667 ImGuiContext& g = *GImGui;
7668 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
7669 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7670 if (popup->Flags & ImGuiWindowFlags_Modal)
7671 return popup;
7672 return NULL;
7673 }
7674
OpenPopup(const char * str_id,ImGuiPopupFlags popup_flags)7675 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
7676 {
7677 ImGuiContext& g = *GImGui;
7678 OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags);
7679 }
7680
7681 // Mark popup as open (toggle toward open state).
7682 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7683 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7684 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id,ImGuiPopupFlags popup_flags)7685 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
7686 {
7687 ImGuiContext& g = *GImGui;
7688 ImGuiWindow* parent_window = g.CurrentWindow;
7689 const int current_stack_size = g.BeginPopupStack.Size;
7690
7691 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
7692 if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId))
7693 return;
7694
7695 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7696 popup_ref.PopupId = id;
7697 popup_ref.Window = NULL;
7698 popup_ref.SourceWindow = g.NavWindow;
7699 popup_ref.OpenFrameCount = g.FrameCount;
7700 popup_ref.OpenParentId = parent_window->IDStack.back();
7701 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7702 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7703
7704 IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id);
7705 if (g.OpenPopupStack.Size < current_stack_size + 1)
7706 {
7707 g.OpenPopupStack.push_back(popup_ref);
7708 }
7709 else
7710 {
7711 // 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
7712 // 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
7713 // 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.
7714 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7715 {
7716 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7717 }
7718 else
7719 {
7720 // Close child popups if any, then flag popup for open/reopen
7721 ClosePopupToLevel(current_stack_size, false);
7722 g.OpenPopupStack.push_back(popup_ref);
7723 }
7724
7725 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7726 // This is equivalent to what ClosePopupToLevel() does.
7727 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7728 // FocusWindow(parent_window);
7729 }
7730 }
7731
7732 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7733 // This function closes any popups that are over 'ref_window'.
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7734 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7735 {
7736 ImGuiContext& g = *GImGui;
7737 if (g.OpenPopupStack.Size == 0)
7738 return;
7739
7740 // Don't close our own child popup windows.
7741 int popup_count_to_keep = 0;
7742 if (ref_window)
7743 {
7744 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7745 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7746 {
7747 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7748 if (!popup.Window)
7749 continue;
7750 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7751 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7752 continue;
7753
7754 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
7755 // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:
7756 // Window -> Popup1 -> Popup2 -> Popup3
7757 // - Each popups may contain child windows, which is why we compare ->RootWindow!
7758 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
7759 bool ref_window_is_descendent_of_popup = false;
7760 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
7761 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
7762 if (popup_window->RootWindow == ref_window->RootWindow)
7763 {
7764 ref_window_is_descendent_of_popup = true;
7765 break;
7766 }
7767 if (!ref_window_is_descendent_of_popup)
7768 break;
7769 }
7770 }
7771 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
7772 {
7773 IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7774 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7775 }
7776 }
7777
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7778 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7779 {
7780 ImGuiContext& g = *GImGui;
7781 IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup);
7782 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7783
7784 // Trim open popup stack
7785 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7786 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7787 g.OpenPopupStack.resize(remaining);
7788
7789 if (restore_focus_to_window_under_popup)
7790 {
7791 if (focus_window && !focus_window->WasActive && popup_window)
7792 {
7793 // Fallback
7794 FocusTopMostWindowUnderOne(popup_window, NULL);
7795 }
7796 else
7797 {
7798 if (g.NavLayer == ImGuiNavLayer_Main && focus_window)
7799 focus_window = NavRestoreLastChildNavWindow(focus_window);
7800 FocusWindow(focus_window);
7801 }
7802 }
7803 }
7804
7805 // Close the popup we have begin-ed into.
CloseCurrentPopup()7806 void ImGui::CloseCurrentPopup()
7807 {
7808 ImGuiContext& g = *GImGui;
7809 int popup_idx = g.BeginPopupStack.Size - 1;
7810 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7811 return;
7812
7813 // Closing a menu closes its top-most parent popup (unless a modal)
7814 while (popup_idx > 0)
7815 {
7816 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7817 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7818 bool close_parent = false;
7819 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7820 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7821 close_parent = true;
7822 if (!close_parent)
7823 break;
7824 popup_idx--;
7825 }
7826 IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7827 ClosePopupToLevel(popup_idx, true);
7828
7829 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7830 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7831 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7832 if (ImGuiWindow* window = g.NavWindow)
7833 window->DC.NavHideHighlightOneFrame = true;
7834 }
7835
7836 // Attention! BeginPopup() adds default flags which BeginPopupEx()!
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)7837 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
7838 {
7839 ImGuiContext& g = *GImGui;
7840 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
7841 {
7842 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7843 return false;
7844 }
7845
7846 char name[20];
7847 if (flags & ImGuiWindowFlags_ChildMenu)
7848 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7849 else
7850 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7851
7852 flags |= ImGuiWindowFlags_Popup;
7853 bool is_open = Begin(name, NULL, flags);
7854 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7855 EndPopup();
7856
7857 return is_open;
7858 }
7859
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7860 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7861 {
7862 ImGuiContext& g = *GImGui;
7863 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7864 {
7865 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7866 return false;
7867 }
7868 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7869 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7870 }
7871
7872 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7873 // Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here.
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)7874 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7875 {
7876 ImGuiContext& g = *GImGui;
7877 ImGuiWindow* window = g.CurrentWindow;
7878 const ImGuiID id = window->GetID(name);
7879 if (!IsPopupOpen(id, ImGuiPopupFlags_None))
7880 {
7881 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7882 return false;
7883 }
7884
7885 // Center modal windows by default for increased visibility
7886 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
7887 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7888 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7889 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
7890
7891 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
7892 const bool is_open = Begin(name, p_open, flags);
7893 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7894 {
7895 EndPopup();
7896 if (is_open)
7897 ClosePopupToLevel(g.BeginPopupStack.Size, true);
7898 return false;
7899 }
7900 return is_open;
7901 }
7902
EndPopup()7903 void ImGui::EndPopup()
7904 {
7905 ImGuiContext& g = *GImGui;
7906 ImGuiWindow* window = g.CurrentWindow;
7907 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
7908 IM_ASSERT(g.BeginPopupStack.Size > 0);
7909
7910 // Make all menus and popups wrap around for now, may need to expose that policy.
7911 if (g.NavWindow == window)
7912 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
7913
7914 // Child-popups don't need to be laid out
7915 IM_ASSERT(g.WithinEndChild == false);
7916 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7917 g.WithinEndChild = true;
7918 End();
7919 g.WithinEndChild = false;
7920 }
7921
7922 // Open a popup if mouse button is released over the item
OpenPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)7923 bool ImGui::OpenPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
7924 {
7925 ImGuiWindow* window = GImGui->CurrentWindow;
7926 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
7927 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7928 {
7929 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
7930 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7931 OpenPopupEx(id, popup_flags);
7932 return true;
7933 }
7934 return false;
7935 }
7936
7937 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7938 // - You can pass a NULL str_id to use the identifier of the last item.
7939 // - You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7940 // - This is essentially the same as calling OpenPopupContextItem() + BeginPopup() but written to avoid
7941 // computing the ID twice because BeginPopupContextXXX functions are called very frequently.
BeginPopupContextItem(const char * str_id,ImGuiPopupFlags popup_flags)7942 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
7943 {
7944 ImGuiWindow* window = GImGui->CurrentWindow;
7945 if (window->SkipItems)
7946 return false;
7947 ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
7948 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7949 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
7950 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7951 OpenPopupEx(id, popup_flags);
7952 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
7953 }
7954
BeginPopupContextWindow(const char * str_id,ImGuiPopupFlags popup_flags)7955 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
7956 {
7957 ImGuiWindow* window = GImGui->CurrentWindow;
7958 if (!str_id)
7959 str_id = "window_context";
7960 ImGuiID id = window->GetID(str_id);
7961 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
7962 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7963 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
7964 OpenPopupEx(id, popup_flags);
7965 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
7966 }
7967
BeginPopupContextVoid(const char * str_id,ImGuiPopupFlags popup_flags)7968 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
7969 {
7970 ImGuiWindow* window = GImGui->CurrentWindow;
7971 if (!str_id)
7972 str_id = "void_context";
7973 ImGuiID id = window->GetID(str_id);
7974 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
7975 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7976 if (GetTopMostPopupModal() == NULL)
7977 OpenPopupEx(id, popup_flags);
7978 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
7979 }
7980
7981 // 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.)
7982 // 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.
FindBestWindowPosForPopupEx(const ImVec2 & ref_pos,const ImVec2 & size,ImGuiDir * last_dir,const ImRect & r_outer,const ImRect & r_avoid,ImGuiPopupPositionPolicy policy)7983 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7984 {
7985 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7986 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7987 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7988
7989 // Combo Box policy (we want a connecting edge)
7990 if (policy == ImGuiPopupPositionPolicy_ComboBox)
7991 {
7992 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7993 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7994 {
7995 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7996 if (n != -1 && dir == *last_dir) // Already tried this direction?
7997 continue;
7998 ImVec2 pos;
7999 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
8000 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
8001 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
8002 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
8003 if (!r_outer.Contains(ImRect(pos, pos + size)))
8004 continue;
8005 *last_dir = dir;
8006 return pos;
8007 }
8008 }
8009
8010 // Default popup policy
8011 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
8012 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
8013 {
8014 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
8015 if (n != -1 && dir == *last_dir) // Already tried this direction?
8016 continue;
8017 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);
8018 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);
8019 if (avail_w < size.x || avail_h < size.y)
8020 continue;
8021 ImVec2 pos;
8022 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
8023 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
8024 *last_dir = dir;
8025 return pos;
8026 }
8027
8028 // Fallback, try to keep within display
8029 *last_dir = ImGuiDir_None;
8030 ImVec2 pos = ref_pos;
8031 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
8032 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
8033 return pos;
8034 }
8035
GetWindowAllowedExtentRect(ImGuiWindow * window)8036 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
8037 {
8038 IM_UNUSED(window);
8039 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
8040 ImRect r_screen = GetViewportRect();
8041 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
8042 return r_screen;
8043 }
8044
FindBestWindowPosForPopup(ImGuiWindow * window)8045 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
8046 {
8047 ImGuiContext& g = *GImGui;
8048
8049 ImRect r_outer = GetWindowAllowedExtentRect(window);
8050 if (window->Flags & ImGuiWindowFlags_ChildMenu)
8051 {
8052 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
8053 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
8054 IM_ASSERT(g.CurrentWindow == window);
8055 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
8056 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).
8057 ImRect r_avoid;
8058 if (parent_window->DC.MenuBarAppending)
8059 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
8060 else
8061 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);
8062 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
8063 }
8064 if (window->Flags & ImGuiWindowFlags_Popup)
8065 {
8066 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
8067 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
8068 }
8069 if (window->Flags & ImGuiWindowFlags_Tooltip)
8070 {
8071 // Position tooltip (always follows mouse)
8072 float sc = g.Style.MouseCursorScale;
8073 ImVec2 ref_pos = NavCalcPreferredRefPos();
8074 ImRect r_avoid;
8075 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
8076 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
8077 else
8078 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.
8079 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
8080 if (window->AutoPosLastDirection == ImGuiDir_None)
8081 pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
8082 return pos;
8083 }
8084 IM_ASSERT(0);
8085 return window->Pos;
8086 }
8087
8088 //-----------------------------------------------------------------------------
8089 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
8090 //-----------------------------------------------------------------------------
8091
8092 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
8093 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)8094 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
8095 {
8096 ImGuiContext& g = *GImGui;
8097 IM_ASSERT(g.NavWindow);
8098 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
8099 g.NavId = id;
8100 g.NavFocusScopeId = focus_scope_id;
8101 g.NavWindow->NavLastIds[nav_layer] = id;
8102 }
8103
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)8104 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
8105 {
8106 ImGuiContext& g = *GImGui;
8107 SetNavID(id, nav_layer, focus_scope_id);
8108 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
8109 g.NavMousePosDirty = true;
8110 g.NavDisableHighlight = false;
8111 g.NavDisableMouseHover = true;
8112 }
8113
SetFocusID(ImGuiID id,ImGuiWindow * window)8114 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
8115 {
8116 ImGuiContext& g = *GImGui;
8117 IM_ASSERT(id != 0);
8118
8119 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
8120 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
8121 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
8122 if (g.NavWindow != window)
8123 g.NavInitRequest = false;
8124 g.NavWindow = window;
8125 g.NavId = id;
8126 g.NavLayer = nav_layer;
8127 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8128 window->NavLastIds[nav_layer] = id;
8129 if (window->DC.LastItemId == id)
8130 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
8131
8132 if (g.ActiveIdSource == ImGuiInputSource_Nav)
8133 g.NavDisableMouseHover = true;
8134 else
8135 g.NavDisableHighlight = true;
8136 }
8137
ImGetDirQuadrantFromDelta(float dx,float dy)8138 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
8139 {
8140 if (ImFabs(dx) > ImFabs(dy))
8141 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
8142 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
8143 }
8144
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)8145 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
8146 {
8147 if (a1 < b0)
8148 return a1 - b0;
8149 if (b1 < a0)
8150 return a0 - b1;
8151 return 0.0f;
8152 }
8153
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)8154 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
8155 {
8156 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
8157 {
8158 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
8159 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
8160 }
8161 else
8162 {
8163 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
8164 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
8165 }
8166 }
8167
8168 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)8169 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
8170 {
8171 ImGuiContext& g = *GImGui;
8172 ImGuiWindow* window = g.CurrentWindow;
8173 if (g.NavLayer != window->DC.NavLayerCurrent)
8174 return false;
8175
8176 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)
8177 g.NavScoringCount++;
8178
8179 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
8180 if (window->ParentWindow == g.NavWindow)
8181 {
8182 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
8183 if (!window->ClipRect.Overlaps(cand))
8184 return false;
8185 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
8186 }
8187
8188 // 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)
8189 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
8190 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
8191
8192 // Compute distance between boxes
8193 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
8194 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
8195 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
8196 if (dby != 0.0f && dbx != 0.0f)
8197 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
8198 float dist_box = ImFabs(dbx) + ImFabs(dby);
8199
8200 // 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)
8201 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
8202 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
8203 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
8204
8205 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
8206 ImGuiDir quadrant;
8207 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
8208 if (dbx != 0.0f || dby != 0.0f)
8209 {
8210 // For non-overlapping boxes, use distance between boxes
8211 dax = dbx;
8212 day = dby;
8213 dist_axial = dist_box;
8214 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
8215 }
8216 else if (dcx != 0.0f || dcy != 0.0f)
8217 {
8218 // For overlapping boxes with different centers, use distance between centers
8219 dax = dcx;
8220 day = dcy;
8221 dist_axial = dist_center;
8222 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
8223 }
8224 else
8225 {
8226 // 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)
8227 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
8228 }
8229
8230 #if IMGUI_DEBUG_NAV_SCORING
8231 char buf[128];
8232 if (IsMouseHoveringRect(cand.Min, cand.Max))
8233 {
8234 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]);
8235 ImDrawList* draw_list = GetForegroundDrawList(window);
8236 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
8237 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
8238 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150));
8239 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
8240 }
8241 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
8242 {
8243 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
8244 if (quadrant == g.NavMoveDir)
8245 {
8246 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
8247 ImDrawList* draw_list = GetForegroundDrawList(window);
8248 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
8249 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
8250 }
8251 }
8252 #endif
8253
8254 // Is it in the quadrant we're interesting in moving to?
8255 bool new_best = false;
8256 if (quadrant == g.NavMoveDir)
8257 {
8258 // Does it beat the current best candidate?
8259 if (dist_box < result->DistBox)
8260 {
8261 result->DistBox = dist_box;
8262 result->DistCenter = dist_center;
8263 return true;
8264 }
8265 if (dist_box == result->DistBox)
8266 {
8267 // Try using distance between center points to break ties
8268 if (dist_center < result->DistCenter)
8269 {
8270 result->DistCenter = dist_center;
8271 new_best = true;
8272 }
8273 else if (dist_center == result->DistCenter)
8274 {
8275 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8276 // (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),
8277 // 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.
8278 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8279 new_best = true;
8280 }
8281 }
8282 }
8283
8284 // 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
8285 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8286 // 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.
8287 // 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.
8288 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8289 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
8290 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8291 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))
8292 {
8293 result->DistAxial = dist_axial;
8294 new_best = true;
8295 }
8296
8297 return new_best;
8298 }
8299
8300 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
NavProcessItem(ImGuiWindow * window,const ImRect & nav_bb,const ImGuiID id)8301 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8302 {
8303 ImGuiContext& g = *GImGui;
8304 //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.
8305 // return;
8306
8307 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
8308 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8309
8310 // Process Init Request
8311 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8312 {
8313 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8314 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
8315 {
8316 g.NavInitResultId = id;
8317 g.NavInitResultRectRel = nav_bb_rel;
8318 }
8319 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
8320 {
8321 g.NavInitRequest = false; // Found a match, clear request
8322 NavUpdateAnyRequestFlag();
8323 }
8324 }
8325
8326 // Process Move Request (scoring for navigation)
8327 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
8328 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav)))
8329 {
8330 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8331 #if IMGUI_DEBUG_NAV_SCORING
8332 // [DEBUG] Score all items in NavWindow at all times
8333 if (!g.NavMoveRequest)
8334 g.NavMoveDir = g.NavMoveDirLast;
8335 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
8336 #else
8337 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
8338 #endif
8339 if (new_best)
8340 {
8341 result->Window = window;
8342 result->ID = id;
8343 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8344 result->RectRel = nav_bb_rel;
8345 }
8346
8347 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8348 const float VISIBLE_RATIO = 0.70f;
8349 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8350 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)
8351 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
8352 {
8353 result = &g.NavMoveResultLocalVisibleSet;
8354 result->Window = window;
8355 result->ID = id;
8356 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8357 result->RectRel = nav_bb_rel;
8358 }
8359 }
8360
8361 // Update window-relative bounding box of navigated item
8362 if (g.NavId == id)
8363 {
8364 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8365 g.NavLayer = window->DC.NavLayerCurrent;
8366 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8367 g.NavIdIsAlive = true;
8368 g.NavIdTabCounter = window->DC.FocusCounterTabStop;
8369 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
8370 }
8371 }
8372
NavMoveRequestButNoResultYet()8373 bool ImGui::NavMoveRequestButNoResultYet()
8374 {
8375 ImGuiContext& g = *GImGui;
8376 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8377 }
8378
NavMoveRequestCancel()8379 void ImGui::NavMoveRequestCancel()
8380 {
8381 ImGuiContext& g = *GImGui;
8382 g.NavMoveRequest = false;
8383 NavUpdateAnyRequestFlag();
8384 }
8385
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)8386 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
8387 {
8388 ImGuiContext& g = *GImGui;
8389 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
8390 NavMoveRequestCancel();
8391 g.NavMoveDir = move_dir;
8392 g.NavMoveClipDir = clip_dir;
8393 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
8394 g.NavMoveRequestFlags = move_flags;
8395 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
8396 }
8397
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)8398 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
8399 {
8400 ImGuiContext& g = *GImGui;
8401
8402 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
8403 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
8404 g.NavWrapRequestWindow = window;
8405 g.NavWrapRequestFlags = move_flags;
8406 }
8407
8408 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8409 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8410 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8411 {
8412 ImGuiWindow* parent = nav_window;
8413 while (parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8414 parent = parent->ParentWindow;
8415 if (parent && parent != nav_window)
8416 parent->NavLastChildNavWindow = nav_window;
8417 }
8418
8419 // Restore the last focused child.
8420 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8421 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8422 {
8423 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
8424 return window->NavLastChildNavWindow;
8425 return window;
8426 }
8427
NavRestoreLayer(ImGuiNavLayer layer)8428 static void NavRestoreLayer(ImGuiNavLayer layer)
8429 {
8430 ImGuiContext& g = *GImGui;
8431 g.NavLayer = layer;
8432 if (layer == 0)
8433 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8434 ImGuiWindow* window = g.NavWindow;
8435 if (layer == 0 && window->NavLastIds[0] != 0)
8436 ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]);
8437 else
8438 ImGui::NavInitWindow(window, true);
8439 }
8440
NavUpdateAnyRequestFlag()8441 static inline void ImGui::NavUpdateAnyRequestFlag()
8442 {
8443 ImGuiContext& g = *GImGui;
8444 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8445 if (g.NavAnyRequest)
8446 IM_ASSERT(g.NavWindow != NULL);
8447 }
8448
8449 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8450 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8451 {
8452 ImGuiContext& g = *GImGui;
8453 IM_ASSERT(window == g.NavWindow);
8454 bool init_for_nav = false;
8455 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8456 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8457 init_for_nav = true;
8458 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8459 if (init_for_nav)
8460 {
8461 SetNavID(0, g.NavLayer, 0);
8462 g.NavInitRequest = true;
8463 g.NavInitRequestFromMove = false;
8464 g.NavInitResultId = 0;
8465 g.NavInitResultRectRel = ImRect();
8466 NavUpdateAnyRequestFlag();
8467 }
8468 else
8469 {
8470 g.NavId = window->NavLastIds[0];
8471 g.NavFocusScopeId = 0;
8472 }
8473 }
8474
NavCalcPreferredRefPos()8475 static ImVec2 ImGui::NavCalcPreferredRefPos()
8476 {
8477 ImGuiContext& g = *GImGui;
8478 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8479 {
8480 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8481 if (IsMousePosValid(&g.IO.MousePos))
8482 return g.IO.MousePos;
8483 return g.LastValidMousePos;
8484 }
8485 else
8486 {
8487 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8488 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8489 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()));
8490 ImRect visible_rect = GetViewportRect();
8491 return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta.
8492 }
8493 }
8494
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8495 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8496 {
8497 ImGuiContext& g = *GImGui;
8498 if (mode == ImGuiInputReadMode_Down)
8499 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
8500
8501 const float t = g.IO.NavInputsDownDuration[n];
8502 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
8503 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8504 if (t < 0.0f)
8505 return 0.0f;
8506 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
8507 return (t == 0.0f) ? 1.0f : 0.0f;
8508 if (mode == ImGuiInputReadMode_Repeat)
8509 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8510 if (mode == ImGuiInputReadMode_RepeatSlow)
8511 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8512 if (mode == ImGuiInputReadMode_RepeatFast)
8513 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8514 return 0.0f;
8515 }
8516
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8517 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8518 {
8519 ImVec2 delta(0.0f, 0.0f);
8520 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8521 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
8522 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8523 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
8524 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8525 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8526 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8527 delta *= slow_factor;
8528 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8529 delta *= fast_factor;
8530 return delta;
8531 }
8532
NavUpdate()8533 static void ImGui::NavUpdate()
8534 {
8535 ImGuiContext& g = *GImGui;
8536 ImGuiIO& io = g.IO;
8537
8538 io.WantSetMousePos = false;
8539 g.NavWrapRequestWindow = NULL;
8540 g.NavWrapRequestFlags = ImGuiNavMoveFlags_None;
8541 #if 0
8542 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);
8543 #endif
8544
8545 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
8546 // (do it before we map Keyboard input!)
8547 bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8548 bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8549 if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_NavGamepad)
8550 {
8551 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
8552 || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f)
8553 g.NavInputSource = ImGuiInputSource_NavGamepad;
8554 }
8555
8556 // Update Keyboard->Nav inputs mapping
8557 if (nav_keyboard_active)
8558 {
8559 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
8560 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
8561 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
8562 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
8563 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8564 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8565 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
8566 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8567 if (io.KeyCtrl)
8568 io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8569 if (io.KeyShift)
8570 io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8571 if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
8572 io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
8573 #undef NAV_MAP_KEY
8574 }
8575 memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration));
8576 for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++)
8577 io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f;
8578
8579 // Process navigation init request (select first/default focus)
8580 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
8581 NavUpdateInitResult();
8582 g.NavInitRequest = false;
8583 g.NavInitRequestFromMove = false;
8584 g.NavInitResultId = 0;
8585 g.NavJustMovedToId = 0;
8586
8587 // Process navigation move request
8588 if (g.NavMoveRequest)
8589 NavUpdateMoveResult();
8590
8591 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8592 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8593 {
8594 IM_ASSERT(g.NavMoveRequest);
8595 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8596 g.NavDisableHighlight = false;
8597 g.NavMoveRequestForward = ImGuiNavForward_None;
8598 }
8599
8600 // Apply application mouse position movement, after we had a chance to process move request result.
8601 if (g.NavMousePosDirty && g.NavIdIsAlive)
8602 {
8603 // Set mouse position given our knowledge of the navigated item position from last frame
8604 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8605 {
8606 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8607 {
8608 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos();
8609 io.WantSetMousePos = true;
8610 }
8611 }
8612 g.NavMousePosDirty = false;
8613 }
8614 g.NavIdIsAlive = false;
8615 g.NavJustTabbedId = 0;
8616 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8617
8618 // 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
8619 if (g.NavWindow)
8620 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8621 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
8622 g.NavWindow->NavLastChildNavWindow = NULL;
8623
8624 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8625 NavUpdateWindowing();
8626
8627 // Set output flags for user application
8628 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8629 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8630
8631 // Process NavCancel input (to close a popup, get back to parent, clear focus)
8632 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8633 {
8634 IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n");
8635 if (g.ActiveId != 0)
8636 {
8637 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8638 ClearActiveID();
8639 }
8640 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8641 {
8642 // Exit child window
8643 ImGuiWindow* child_window = g.NavWindow;
8644 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8645 IM_ASSERT(child_window->ChildId != 0);
8646 FocusWindow(parent_window);
8647 SetNavID(child_window->ChildId, 0, 0);
8648 // Reassigning with same value, we're being explicit here.
8649 g.NavIdIsAlive = false; // -V1048
8650 if (g.NavDisableMouseHover)
8651 g.NavMousePosDirty = true;
8652 }
8653 else if (g.OpenPopupStack.Size > 0)
8654 {
8655 // Close open popup/menu
8656 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8657 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8658 }
8659 else if (g.NavLayer != ImGuiNavLayer_Main)
8660 {
8661 // Leave the "menu" layer
8662 NavRestoreLayer(ImGuiNavLayer_Main);
8663 }
8664 else
8665 {
8666 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8667 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8668 g.NavWindow->NavLastIds[0] = 0;
8669 g.NavId = g.NavFocusScopeId = 0;
8670 }
8671 }
8672
8673 // Process manual activation request
8674 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8675 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8676 {
8677 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8678 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8679 if (g.ActiveId == 0 && activate_pressed)
8680 g.NavActivateId = g.NavId;
8681 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8682 g.NavActivateDownId = g.NavId;
8683 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8684 g.NavActivatePressedId = g.NavId;
8685 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8686 g.NavInputId = g.NavId;
8687 }
8688 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8689 g.NavDisableHighlight = true;
8690 if (g.NavActivateId != 0)
8691 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8692 g.NavMoveRequest = false;
8693
8694 // Process programmatic activation request
8695 if (g.NavNextActivateId != 0)
8696 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8697 g.NavNextActivateId = 0;
8698
8699 // Initiate directional inputs request
8700 if (g.NavMoveRequestForward == ImGuiNavForward_None)
8701 {
8702 g.NavMoveDir = ImGuiDir_None;
8703 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8704 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8705 {
8706 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8707 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8708 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8709 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8710 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8711 }
8712 g.NavMoveClipDir = g.NavMoveDir;
8713 }
8714 else
8715 {
8716 // 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)
8717 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8718 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8719 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8720 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
8721 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8722 }
8723
8724 // Update PageUp/PageDown/Home/End scroll
8725 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8726 float nav_scoring_rect_offset_y = 0.0f;
8727 if (nav_keyboard_active)
8728 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8729
8730 // 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
8731 if (g.NavMoveDir != ImGuiDir_None)
8732 {
8733 g.NavMoveRequest = true;
8734 g.NavMoveRequestKeyMods = io.KeyMods;
8735 g.NavMoveDirLast = g.NavMoveDir;
8736 }
8737 if (g.NavMoveRequest && g.NavId == 0)
8738 {
8739 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8740 g.NavInitRequest = g.NavInitRequestFromMove = true;
8741 // Reassigning with same value, we're being explicit here.
8742 g.NavInitResultId = 0; // -V1048
8743 g.NavDisableHighlight = false;
8744 }
8745 NavUpdateAnyRequestFlag();
8746
8747 // Scrolling
8748 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8749 {
8750 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8751 ImGuiWindow* window = g.NavWindow;
8752 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.
8753 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8754 {
8755 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8756 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8757 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8758 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8759 }
8760
8761 // *Normal* Manual scroll with NavScrollXXX keys
8762 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8763 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f);
8764 if (scroll_dir.x != 0.0f && window->ScrollbarX)
8765 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8766 if (scroll_dir.y != 0.0f)
8767 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8768 }
8769
8770 // Reset search results
8771 g.NavMoveResultLocal.Clear();
8772 g.NavMoveResultLocalVisibleSet.Clear();
8773 g.NavMoveResultOther.Clear();
8774
8775 // When using gamepad, we project the reference nav bounding box into window visible area.
8776 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative
8777 // (can't focus a visible object like we can with the mouse).
8778 if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_NavGamepad && g.NavLayer == ImGuiNavLayer_Main)
8779 {
8780 ImGuiWindow* window = g.NavWindow;
8781 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1));
8782 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8783 {
8784 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n");
8785 float pad = window->CalcFontSize() * 0.5f;
8786 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
8787 window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel);
8788 g.NavId = g.NavFocusScopeId = 0;
8789 }
8790 }
8791
8792 // 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)
8793 ImRect nav_rect_rel = g.NavWindow ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
8794 g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8795 g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y);
8796 g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x);
8797 g.NavScoringRect.Max.x = g.NavScoringRect.Min.x;
8798 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().
8799 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8800 g.NavScoringCount = 0;
8801 #if IMGUI_DEBUG_NAV_RECTS
8802 if (g.NavWindow)
8803 {
8804 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8805 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]
8806 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); }
8807 }
8808 #endif
8809 }
8810
NavUpdateInitResult()8811 static void ImGui::NavUpdateInitResult()
8812 {
8813 // 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)
8814 ImGuiContext& g = *GImGui;
8815 if (!g.NavWindow)
8816 return;
8817
8818 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8819 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8820 if (g.NavInitRequestFromMove)
8821 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
8822 else
8823 SetNavID(g.NavInitResultId, g.NavLayer, 0);
8824 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8825 }
8826
8827 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8828 static void ImGui::NavUpdateMoveResult()
8829 {
8830 ImGuiContext& g = *GImGui;
8831 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8832 {
8833 // 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)
8834 if (g.NavId != 0)
8835 {
8836 g.NavDisableHighlight = false;
8837 g.NavDisableMouseHover = true;
8838 }
8839 return;
8840 }
8841
8842 // Select which result to use
8843 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8844
8845 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8846 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8847 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8848 result = &g.NavMoveResultLocalVisibleSet;
8849
8850 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8851 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8852 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8853 result = &g.NavMoveResultOther;
8854 IM_ASSERT(g.NavWindow && result->Window);
8855
8856 // Scroll to keep newly navigated item fully into view.
8857 if (g.NavLayer == ImGuiNavLayer_Main)
8858 {
8859 ImVec2 delta_scroll;
8860 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8861 {
8862 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8863 delta_scroll.y = result->Window->Scroll.y - scroll_target;
8864 SetScrollY(result->Window, scroll_target);
8865 }
8866 else
8867 {
8868 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8869 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8870 }
8871
8872 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8873 result->RectRel.TranslateX(-delta_scroll.x);
8874 result->RectRel.TranslateY(-delta_scroll.y);
8875 }
8876
8877 ClearActiveID();
8878 g.NavWindow = result->Window;
8879 if (g.NavId != result->ID)
8880 {
8881 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8882 g.NavJustMovedToId = result->ID;
8883 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
8884 g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods;
8885 }
8886 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
8887 SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
8888 }
8889
8890 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8891 static float ImGui::NavUpdatePageUpPageDown()
8892 {
8893 ImGuiContext& g = *GImGui;
8894 ImGuiIO& io = g.IO;
8895
8896 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8897 return 0.0f;
8898 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main)
8899 return 0.0f;
8900
8901 ImGuiWindow* window = g.NavWindow;
8902 const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8903 const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8904 const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8905 const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8906 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8907 {
8908 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8909 {
8910 // Fallback manual-scroll when window has no navigable item
8911 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
8912 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8913 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
8914 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8915 else if (home_pressed)
8916 SetScrollY(window, 0.0f);
8917 else if (end_pressed)
8918 SetScrollY(window, window->ScrollMax.y);
8919 }
8920 else
8921 {
8922 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8923 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8924 float nav_scoring_rect_offset_y = 0.0f;
8925 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
8926 {
8927 nav_scoring_rect_offset_y = -page_offset_y;
8928 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)
8929 g.NavMoveClipDir = ImGuiDir_Up;
8930 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8931 }
8932 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
8933 {
8934 nav_scoring_rect_offset_y = +page_offset_y;
8935 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)
8936 g.NavMoveClipDir = ImGuiDir_Down;
8937 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8938 }
8939 else if (home_pressed)
8940 {
8941 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8942 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8943 // Preserve current horizontal position if we have any.
8944 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8945 if (nav_rect_rel.IsInverted())
8946 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8947 g.NavMoveDir = ImGuiDir_Down;
8948 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8949 }
8950 else if (end_pressed)
8951 {
8952 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8953 if (nav_rect_rel.IsInverted())
8954 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8955 g.NavMoveDir = ImGuiDir_Up;
8956 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8957 }
8958 return nav_scoring_rect_offset_y;
8959 }
8960 }
8961 return 0.0f;
8962 }
8963
NavEndFrame()8964 static void ImGui::NavEndFrame()
8965 {
8966 ImGuiContext& g = *GImGui;
8967
8968 // Show CTRL+TAB list window
8969 if (g.NavWindowingTarget != NULL)
8970 NavUpdateWindowingOverlay();
8971
8972 // Perform wrap-around in menus
8973 ImGuiWindow* window = g.NavWrapRequestWindow;
8974 ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags;
8975 if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main)
8976 {
8977 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
8978 ImRect bb_rel = window->NavRectRel[0];
8979
8980 ImGuiDir clip_dir = g.NavMoveDir;
8981 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8982 {
8983 bb_rel.Min.x = bb_rel.Max.x =
8984 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
8985 if (move_flags & ImGuiNavMoveFlags_WrapX)
8986 {
8987 bb_rel.TranslateY(-bb_rel.GetHeight());
8988 clip_dir = ImGuiDir_Up;
8989 }
8990 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8991 }
8992 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8993 {
8994 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
8995 if (move_flags & ImGuiNavMoveFlags_WrapX)
8996 {
8997 bb_rel.TranslateY(+bb_rel.GetHeight());
8998 clip_dir = ImGuiDir_Down;
8999 }
9000 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9001 }
9002 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9003 {
9004 bb_rel.Min.y = bb_rel.Max.y =
9005 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
9006 if (move_flags & ImGuiNavMoveFlags_WrapY)
9007 {
9008 bb_rel.TranslateX(-bb_rel.GetWidth());
9009 clip_dir = ImGuiDir_Left;
9010 }
9011 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9012 }
9013 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
9014 {
9015 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
9016 if (move_flags & ImGuiNavMoveFlags_WrapY)
9017 {
9018 bb_rel.TranslateX(+bb_rel.GetWidth());
9019 clip_dir = ImGuiDir_Right;
9020 }
9021 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
9022 }
9023 }
9024 }
9025
FindWindowFocusIndex(ImGuiWindow * window)9026 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
9027 {
9028 ImGuiContext& g = *GImGui;
9029 for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
9030 if (g.WindowsFocusOrder[i] == window)
9031 return i;
9032 return -1;
9033 }
9034
FindWindowNavFocusable(int i_start,int i_stop,int dir)9035 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
9036 {
9037 ImGuiContext& g = *GImGui;
9038 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
9039 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
9040 return g.WindowsFocusOrder[i];
9041 return NULL;
9042 }
9043
NavUpdateWindowingHighlightWindow(int focus_change_dir)9044 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
9045 {
9046 ImGuiContext& g = *GImGui;
9047 IM_ASSERT(g.NavWindowingTarget);
9048 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
9049 return;
9050
9051 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
9052 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
9053 if (!window_target)
9054 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
9055 if (window_target) // Don't reset windowing target if there's a single window in the list
9056 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
9057 g.NavWindowingToggleLayer = false;
9058 }
9059
9060 // Windowing management mode
9061 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
9062 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()9063 static void ImGui::NavUpdateWindowing()
9064 {
9065 ImGuiContext& g = *GImGui;
9066 ImGuiWindow* apply_focus_window = NULL;
9067 bool apply_toggle_layer = false;
9068
9069 ImGuiWindow* modal_window = GetTopMostPopupModal();
9070 bool allow_windowing = (modal_window == NULL);
9071 if (!allow_windowing)
9072 g.NavWindowingTarget = NULL;
9073
9074 // Fade out
9075 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
9076 {
9077 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
9078 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
9079 g.NavWindowingTargetAnim = NULL;
9080 }
9081
9082 // Start CTRL-TAB or Square+L/R window selection
9083 bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
9084 bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
9085 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
9086 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
9087 {
9088 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop
9089 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
9090 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
9091 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
9092 }
9093
9094 // Gamepad update
9095 g.NavWindowingTimer += g.IO.DeltaTime;
9096 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
9097 {
9098 // 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
9099 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
9100
9101 // Select window to focus
9102 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
9103 if (focus_change_dir != 0)
9104 {
9105 NavUpdateWindowingHighlightWindow(focus_change_dir);
9106 g.NavWindowingHighlightAlpha = 1.0f;
9107 }
9108
9109 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
9110 if (!IsNavInputDown(ImGuiNavInput_Menu))
9111 {
9112 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
9113 if (g.NavWindowingToggleLayer && g.NavWindow)
9114 apply_toggle_layer = true;
9115 else if (!g.NavWindowingToggleLayer)
9116 apply_focus_window = g.NavWindowingTarget;
9117 g.NavWindowingTarget = NULL;
9118 }
9119 }
9120
9121 // Keyboard: Focus
9122 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
9123 {
9124 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
9125 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
9126 if (IsKeyPressedMap(ImGuiKey_Tab, true))
9127 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
9128 if (!g.IO.KeyCtrl)
9129 apply_focus_window = g.NavWindowingTarget;
9130 }
9131
9132 // Keyboard: Press and Release ALT to toggle menu layer
9133 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB
9134 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
9135 g.NavWindowingToggleLayer = true;
9136 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
9137 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
9138 apply_toggle_layer = true;
9139
9140 // Move window
9141 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
9142 {
9143 ImVec2 move_delta;
9144 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
9145 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
9146 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
9147 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
9148 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
9149 {
9150 const float NAV_MOVE_SPEED = 800.0f;
9151 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
9152 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
9153 SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always);
9154 MarkIniSettingsDirty(moving_window);
9155 g.NavDisableMouseHover = true;
9156 }
9157 }
9158
9159 // Apply final focus
9160 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
9161 {
9162 ClearActiveID();
9163 g.NavDisableHighlight = false;
9164 g.NavDisableMouseHover = true;
9165 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
9166 ClosePopupsOverWindow(apply_focus_window, false);
9167 FocusWindow(apply_focus_window);
9168 if (apply_focus_window->NavLastIds[0] == 0)
9169 NavInitWindow(apply_focus_window, false);
9170
9171 // If the window only has a menu layer, select it directly
9172 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
9173 g.NavLayer = ImGuiNavLayer_Menu;
9174 }
9175 if (apply_focus_window)
9176 g.NavWindowingTarget = NULL;
9177
9178 // Apply menu/layer toggle
9179 if (apply_toggle_layer && g.NavWindow)
9180 {
9181 // Move to parent menu if necessary
9182 ImGuiWindow* new_nav_window = g.NavWindow;
9183 while (new_nav_window->ParentWindow
9184 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
9185 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
9186 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
9187 new_nav_window = new_nav_window->ParentWindow;
9188 if (new_nav_window != g.NavWindow)
9189 {
9190 ImGuiWindow* old_nav_window = g.NavWindow;
9191 FocusWindow(new_nav_window);
9192 new_nav_window->NavLastChildNavWindow = old_nav_window;
9193 }
9194 g.NavDisableHighlight = false;
9195 g.NavDisableMouseHover = true;
9196
9197 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
9198 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
9199 NavRestoreLayer(new_nav_layer);
9200 }
9201 }
9202
9203 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)9204 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
9205 {
9206 if (window->Flags & ImGuiWindowFlags_Popup)
9207 return "(Popup)";
9208 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
9209 return "(Main menu bar)";
9210 return "(Untitled)";
9211 }
9212
9213 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()9214 void ImGui::NavUpdateWindowingOverlay()
9215 {
9216 ImGuiContext& g = *GImGui;
9217 IM_ASSERT(g.NavWindowingTarget != NULL);
9218
9219 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
9220 return;
9221
9222 if (g.NavWindowingListWindow == NULL)
9223 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList");
9224 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
9225 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9226 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
9227 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
9228 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
9229 {
9230 ImGuiWindow* window = g.WindowsFocusOrder[n];
9231 if (!IsWindowNavFocusable(window))
9232 continue;
9233 const char* label = window->Name;
9234 if (label == FindRenderedTextEnd(label))
9235 label = GetFallbackWindowNameForWindowingList(window);
9236 Selectable(label, g.NavWindowingTarget == window);
9237 }
9238 End();
9239 PopStyleVar();
9240 }
9241
9242
9243 //-----------------------------------------------------------------------------
9244 // [SECTION] DRAG AND DROP
9245 //-----------------------------------------------------------------------------
9246
ClearDragDrop()9247 void ImGui::ClearDragDrop()
9248 {
9249 ImGuiContext& g = *GImGui;
9250 g.DragDropActive = false;
9251 g.DragDropPayload.Clear();
9252 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
9253 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
9254 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
9255 g.DragDropAcceptFrameCount = -1;
9256
9257 g.DragDropPayloadBufHeap.clear();
9258 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9259 }
9260
9261 // Call when current ID is active.
9262 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
BeginDragDropSource(ImGuiDragDropFlags flags)9263 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
9264 {
9265 ImGuiContext& g = *GImGui;
9266 ImGuiWindow* window = g.CurrentWindow;
9267
9268 bool source_drag_active = false;
9269 ImGuiID source_id = 0;
9270 ImGuiID source_parent_id = 0;
9271 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
9272 if (!(flags & ImGuiDragDropFlags_SourceExtern))
9273 {
9274 source_id = window->DC.LastItemId;
9275 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
9276 return false;
9277 if (g.IO.MouseDown[mouse_button] == false)
9278 return false;
9279
9280 if (source_id == 0)
9281 {
9282 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
9283 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
9284 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
9285 {
9286 IM_ASSERT(0);
9287 return false;
9288 }
9289
9290 // Early out
9291 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
9292 return false;
9293
9294 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
9295 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
9296 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
9297 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
9298 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
9299 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
9300 if (is_hovered && g.IO.MouseClicked[mouse_button])
9301 {
9302 SetActiveID(source_id, window);
9303 FocusWindow(window);
9304 }
9305 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
9306 g.ActiveIdAllowOverlap = is_hovered;
9307 }
9308 else
9309 {
9310 g.ActiveIdAllowOverlap = false;
9311 }
9312 if (g.ActiveId != source_id)
9313 return false;
9314 source_parent_id = window->IDStack.back();
9315 source_drag_active = IsMouseDragging(mouse_button);
9316
9317 // Disable navigation and key inputs while dragging
9318 g.ActiveIdUsingNavDirMask = ~(ImU32)0;
9319 g.ActiveIdUsingNavInputMask = ~(ImU32)0;
9320 g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
9321 }
9322 else
9323 {
9324 window = NULL;
9325 source_id = ImHashStr("#SourceExtern");
9326 source_drag_active = true;
9327 }
9328
9329 if (source_drag_active)
9330 {
9331 if (!g.DragDropActive)
9332 {
9333 IM_ASSERT(source_id != 0);
9334 ClearDragDrop();
9335 ImGuiPayload& payload = g.DragDropPayload;
9336 payload.SourceId = source_id;
9337 payload.SourceParentId = source_parent_id;
9338 g.DragDropActive = true;
9339 g.DragDropSourceFlags = flags;
9340 g.DragDropMouseButton = mouse_button;
9341 }
9342 g.DragDropSourceFrameCount = g.FrameCount;
9343 g.DragDropWithinSource = true;
9344
9345 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9346 {
9347 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
9348 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
9349 BeginTooltip();
9350 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
9351 {
9352 ImGuiWindow* tooltip_window = g.CurrentWindow;
9353 tooltip_window->SkipItems = true;
9354 tooltip_window->HiddenFramesCanSkipItems = 1;
9355 }
9356 }
9357
9358 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
9359 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
9360
9361 return true;
9362 }
9363 return false;
9364 }
9365
EndDragDropSource()9366 void ImGui::EndDragDropSource()
9367 {
9368 ImGuiContext& g = *GImGui;
9369 IM_ASSERT(g.DragDropActive);
9370 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
9371
9372 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9373 EndTooltip();
9374
9375 // Discard the drag if have not called SetDragDropPayload()
9376 if (g.DragDropPayload.DataFrameCount == -1)
9377 ClearDragDrop();
9378 g.DragDropWithinSource = false;
9379 }
9380
9381 // Use 'cond' to choose to submit payload on drag start or every frame
SetDragDropPayload(const char * type,const void * data,size_t data_size,ImGuiCond cond)9382 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
9383 {
9384 ImGuiContext& g = *GImGui;
9385 ImGuiPayload& payload = g.DragDropPayload;
9386 if (cond == 0)
9387 cond = ImGuiCond_Always;
9388
9389 IM_ASSERT(type != NULL);
9390 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
9391 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
9392 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
9393 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
9394
9395 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
9396 {
9397 // Copy payload
9398 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
9399 g.DragDropPayloadBufHeap.resize(0);
9400 if (data_size > sizeof(g.DragDropPayloadBufLocal))
9401 {
9402 // Store in heap
9403 g.DragDropPayloadBufHeap.resize((int)data_size);
9404 payload.Data = g.DragDropPayloadBufHeap.Data;
9405 memcpy(payload.Data, data, data_size);
9406 }
9407 else if (data_size > 0)
9408 {
9409 // Store locally
9410 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9411 payload.Data = g.DragDropPayloadBufLocal;
9412 memcpy(payload.Data, data, data_size);
9413 }
9414 else
9415 {
9416 payload.Data = NULL;
9417 }
9418 payload.DataSize = (int)data_size;
9419 }
9420 payload.DataFrameCount = g.FrameCount;
9421
9422 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
9423 }
9424
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)9425 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
9426 {
9427 ImGuiContext& g = *GImGui;
9428 if (!g.DragDropActive)
9429 return false;
9430
9431 ImGuiWindow* window = g.CurrentWindow;
9432 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
9433 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
9434 return false;
9435 IM_ASSERT(id != 0);
9436 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
9437 return false;
9438 if (window->SkipItems)
9439 return false;
9440
9441 IM_ASSERT(g.DragDropWithinTarget == false);
9442 g.DragDropTargetRect = bb;
9443 g.DragDropTargetId = id;
9444 g.DragDropWithinTarget = true;
9445 return true;
9446 }
9447
9448 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
9449 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
9450 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
9451 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()9452 bool ImGui::BeginDragDropTarget()
9453 {
9454 ImGuiContext& g = *GImGui;
9455 if (!g.DragDropActive)
9456 return false;
9457
9458 ImGuiWindow* window = g.CurrentWindow;
9459 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
9460 return false;
9461 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
9462 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
9463 return false;
9464
9465 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
9466 ImGuiID id = window->DC.LastItemId;
9467 if (id == 0)
9468 id = window->GetIDFromRectangle(display_rect);
9469 if (g.DragDropPayload.SourceId == id)
9470 return false;
9471
9472 IM_ASSERT(g.DragDropWithinTarget == false);
9473 g.DragDropTargetRect = display_rect;
9474 g.DragDropTargetId = id;
9475 g.DragDropWithinTarget = true;
9476 return true;
9477 }
9478
IsDragDropPayloadBeingAccepted()9479 bool ImGui::IsDragDropPayloadBeingAccepted()
9480 {
9481 ImGuiContext& g = *GImGui;
9482 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9483 }
9484
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9485 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9486 {
9487 ImGuiContext& g = *GImGui;
9488 ImGuiWindow* window = g.CurrentWindow;
9489 ImGuiPayload& payload = g.DragDropPayload;
9490 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9491 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
9492 if (type != NULL && !payload.IsDataType(type))
9493 return NULL;
9494
9495 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9496 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9497 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9498 ImRect r = g.DragDropTargetRect;
9499 float r_surface = r.GetWidth() * r.GetHeight();
9500 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
9501 {
9502 g.DragDropAcceptFlags = flags;
9503 g.DragDropAcceptIdCurr = g.DragDropTargetId;
9504 g.DragDropAcceptIdCurrRectSurface = r_surface;
9505 }
9506
9507 // Render default drop visuals
9508 payload.Preview = was_accepted_previously;
9509 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9510 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9511 {
9512 // FIXME-DRAG: Settle on a proper default visuals for drop target.
9513 r.Expand(3.5f);
9514 bool push_clip_rect = !window->ClipRect.Contains(r);
9515 if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1));
9516 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9517 if (push_clip_rect) window->DrawList->PopClipRect();
9518 }
9519
9520 g.DragDropAcceptFrameCount = g.FrameCount;
9521 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()
9522 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9523 return NULL;
9524
9525 return &payload;
9526 }
9527
GetDragDropPayload()9528 const ImGuiPayload* ImGui::GetDragDropPayload()
9529 {
9530 ImGuiContext& g = *GImGui;
9531 return g.DragDropActive ? &g.DragDropPayload : NULL;
9532 }
9533
9534 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9535 void ImGui::EndDragDropTarget()
9536 {
9537 ImGuiContext& g = *GImGui;
9538 IM_ASSERT(g.DragDropActive);
9539 IM_ASSERT(g.DragDropWithinTarget);
9540 g.DragDropWithinTarget = false;
9541 }
9542
9543 //-----------------------------------------------------------------------------
9544 // [SECTION] LOGGING/CAPTURING
9545 //-----------------------------------------------------------------------------
9546 // All text output from the interface can be captured into tty/file/clipboard.
9547 // By default, tree nodes are automatically opened during logging.
9548 //-----------------------------------------------------------------------------
9549
9550 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9551 void ImGui::LogText(const char* fmt, ...)
9552 {
9553 ImGuiContext& g = *GImGui;
9554 if (!g.LogEnabled)
9555 return;
9556
9557 va_list args;
9558 va_start(args, fmt);
9559 if (g.LogFile)
9560 {
9561 g.LogBuffer.Buf.resize(0);
9562 g.LogBuffer.appendfv(fmt, args);
9563 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9564 }
9565 else
9566 {
9567 g.LogBuffer.appendfv(fmt, args);
9568 }
9569 va_end(args);
9570 }
9571
9572 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9573 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9574 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9575 {
9576 ImGuiContext& g = *GImGui;
9577 ImGuiWindow* window = g.CurrentWindow;
9578
9579 if (!text_end)
9580 text_end = FindRenderedTextEnd(text, text_end);
9581
9582 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9583 if (ref_pos)
9584 g.LogLinePosY = ref_pos->y;
9585 if (log_new_line)
9586 g.LogLineFirstItem = true;
9587
9588 const char* text_remaining = text;
9589 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
9590 g.LogDepthRef = window->DC.TreeDepth;
9591 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9592 for (;;)
9593 {
9594 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9595 // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9596 const char* line_start = text_remaining;
9597 const char* line_end = ImStreolRange(line_start, text_end);
9598 const bool is_first_line = (line_start == text);
9599 const bool is_last_line = (line_end == text_end);
9600 if (!is_last_line || (line_start != line_end))
9601 {
9602 const int char_count = (int)(line_end - line_start);
9603 if (log_new_line || !is_first_line)
9604 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9605 else if (g.LogLineFirstItem)
9606 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9607 else
9608 LogText(" %.*s", char_count, line_start);
9609 g.LogLineFirstItem = false;
9610 }
9611 else if (log_new_line)
9612 {
9613 // An empty "" string at a different Y position should output a carriage return.
9614 LogText(IM_NEWLINE);
9615 break;
9616 }
9617
9618 if (is_last_line)
9619 break;
9620 text_remaining = line_end + 1;
9621 }
9622 }
9623
9624 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9625 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9626 {
9627 ImGuiContext& g = *GImGui;
9628 ImGuiWindow* window = g.CurrentWindow;
9629 IM_ASSERT(g.LogEnabled == false);
9630 IM_ASSERT(g.LogFile == NULL);
9631 IM_ASSERT(g.LogBuffer.empty());
9632 g.LogEnabled = true;
9633 g.LogType = type;
9634 g.LogDepthRef = window->DC.TreeDepth;
9635 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9636 g.LogLinePosY = FLT_MAX;
9637 g.LogLineFirstItem = true;
9638 }
9639
LogToTTY(int auto_open_depth)9640 void ImGui::LogToTTY(int auto_open_depth)
9641 {
9642 ImGuiContext& g = *GImGui;
9643 if (g.LogEnabled)
9644 return;
9645 IM_UNUSED(auto_open_depth);
9646 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9647 LogBegin(ImGuiLogType_TTY, auto_open_depth);
9648 g.LogFile = stdout;
9649 #endif
9650 }
9651
9652 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9653 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9654 {
9655 ImGuiContext& g = *GImGui;
9656 if (g.LogEnabled)
9657 return;
9658
9659 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9660 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9661 // By opening the file in binary mode "ab" we have consistent output everywhere.
9662 if (!filename)
9663 filename = g.IO.LogFilename;
9664 if (!filename || !filename[0])
9665 return;
9666 ImFileHandle f = ImFileOpen(filename, "ab");
9667 if (!f)
9668 {
9669 IM_ASSERT(0);
9670 return;
9671 }
9672
9673 LogBegin(ImGuiLogType_File, auto_open_depth);
9674 g.LogFile = f;
9675 }
9676
9677 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9678 void ImGui::LogToClipboard(int auto_open_depth)
9679 {
9680 ImGuiContext& g = *GImGui;
9681 if (g.LogEnabled)
9682 return;
9683 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9684 }
9685
LogToBuffer(int auto_open_depth)9686 void ImGui::LogToBuffer(int auto_open_depth)
9687 {
9688 ImGuiContext& g = *GImGui;
9689 if (g.LogEnabled)
9690 return;
9691 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9692 }
9693
LogFinish()9694 void ImGui::LogFinish()
9695 {
9696 ImGuiContext& g = *GImGui;
9697 if (!g.LogEnabled)
9698 return;
9699
9700 LogText(IM_NEWLINE);
9701 switch (g.LogType)
9702 {
9703 case ImGuiLogType_TTY:
9704 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9705 fflush(g.LogFile);
9706 #endif
9707 break;
9708 case ImGuiLogType_File:
9709 ImFileClose(g.LogFile);
9710 break;
9711 case ImGuiLogType_Buffer:
9712 break;
9713 case ImGuiLogType_Clipboard:
9714 if (!g.LogBuffer.empty())
9715 SetClipboardText(g.LogBuffer.begin());
9716 break;
9717 case ImGuiLogType_None:
9718 IM_ASSERT(0);
9719 break;
9720 }
9721
9722 g.LogEnabled = false;
9723 g.LogType = ImGuiLogType_None;
9724 g.LogFile = NULL;
9725 g.LogBuffer.clear();
9726 }
9727
9728 // Helper to display logging buttons
9729 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9730 void ImGui::LogButtons()
9731 {
9732 ImGuiContext& g = *GImGui;
9733
9734 PushID("LogButtons");
9735 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9736 const bool log_to_tty = Button("Log To TTY"); SameLine();
9737 #else
9738 const bool log_to_tty = false;
9739 #endif
9740 const bool log_to_file = Button("Log To File"); SameLine();
9741 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9742 PushAllowKeyboardFocus(false);
9743 SetNextItemWidth(80.0f);
9744 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9745 PopAllowKeyboardFocus();
9746 PopID();
9747
9748 // Start logging at the end of the function so that the buttons don't appear in the log
9749 if (log_to_tty)
9750 LogToTTY();
9751 if (log_to_file)
9752 LogToFile();
9753 if (log_to_clipboard)
9754 LogToClipboard();
9755 }
9756
9757
9758 //-----------------------------------------------------------------------------
9759 // [SECTION] SETTINGS
9760 //-----------------------------------------------------------------------------
9761 // - UpdateSettings() [Internal]
9762 // - MarkIniSettingsDirty() [Internal]
9763 // - CreateNewWindowSettings() [Internal]
9764 // - FindWindowSettings() [Internal]
9765 // - FindOrCreateWindowSettings() [Internal]
9766 // - FindSettingsHandler() [Internal]
9767 // - ClearIniSettings() [Internal]
9768 // - LoadIniSettingsFromDisk()
9769 // - LoadIniSettingsFromMemory()
9770 // - SaveIniSettingsToDisk()
9771 // - SaveIniSettingsToMemory()
9772 // - WindowSettingsHandler_***() [Internal]
9773 //-----------------------------------------------------------------------------
9774
9775 // Called by NewFrame()
UpdateSettings()9776 void ImGui::UpdateSettings()
9777 {
9778 // Load settings on first frame (if not explicitly loaded manually before)
9779 ImGuiContext& g = *GImGui;
9780 if (!g.SettingsLoaded)
9781 {
9782 IM_ASSERT(g.SettingsWindows.empty());
9783 if (g.IO.IniFilename)
9784 LoadIniSettingsFromDisk(g.IO.IniFilename);
9785 g.SettingsLoaded = true;
9786 }
9787
9788 // Save settings (with a delay after the last modification, so we don't spam disk too much)
9789 if (g.SettingsDirtyTimer > 0.0f)
9790 {
9791 g.SettingsDirtyTimer -= g.IO.DeltaTime;
9792 if (g.SettingsDirtyTimer <= 0.0f)
9793 {
9794 if (g.IO.IniFilename != NULL)
9795 SaveIniSettingsToDisk(g.IO.IniFilename);
9796 else
9797 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
9798 g.SettingsDirtyTimer = 0.0f;
9799 }
9800 }
9801 }
9802
MarkIniSettingsDirty()9803 void ImGui::MarkIniSettingsDirty()
9804 {
9805 ImGuiContext& g = *GImGui;
9806 if (g.SettingsDirtyTimer <= 0.0f)
9807 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9808 }
9809
MarkIniSettingsDirty(ImGuiWindow * window)9810 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9811 {
9812 ImGuiContext& g = *GImGui;
9813 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9814 if (g.SettingsDirtyTimer <= 0.0f)
9815 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9816 }
9817
CreateNewWindowSettings(const char * name)9818 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9819 {
9820 ImGuiContext& g = *GImGui;
9821
9822 #if !IMGUI_DEBUG_INI_SETTINGS
9823 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9824 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9825 if (const char* p = strstr(name, "###"))
9826 name = p;
9827 #endif
9828 const size_t name_len = strlen(name);
9829
9830 // Allocate chunk
9831 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
9832 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
9833 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
9834 settings->ID = ImHashStr(name, name_len);
9835 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
9836
9837 return settings;
9838 }
9839
FindWindowSettings(ImGuiID id)9840 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9841 {
9842 ImGuiContext& g = *GImGui;
9843 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9844 if (settings->ID == id)
9845 return settings;
9846 return NULL;
9847 }
9848
FindOrCreateWindowSettings(const char * name)9849 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9850 {
9851 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9852 return settings;
9853 return CreateNewWindowSettings(name);
9854 }
9855
FindSettingsHandler(const char * type_name)9856 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9857 {
9858 ImGuiContext& g = *GImGui;
9859 const ImGuiID type_hash = ImHashStr(type_name);
9860 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9861 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9862 return &g.SettingsHandlers[handler_n];
9863 return NULL;
9864 }
9865
ClearIniSettings()9866 void ImGui::ClearIniSettings()
9867 {
9868 ImGuiContext& g = *GImGui;
9869 g.SettingsIniData.clear();
9870 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9871 if (g.SettingsHandlers[handler_n].ClearAllFn)
9872 g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]);
9873 }
9874
LoadIniSettingsFromDisk(const char * ini_filename)9875 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9876 {
9877 size_t file_data_size = 0;
9878 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9879 if (!file_data)
9880 return;
9881 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9882 IM_FREE(file_data);
9883 }
9884
9885 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9886 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9887 {
9888 ImGuiContext& g = *GImGui;
9889 IM_ASSERT(g.Initialized);
9890 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
9891 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9892
9893 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9894 // 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..
9895 if (ini_size == 0)
9896 ini_size = strlen(ini_data);
9897 g.SettingsIniData.Buf.resize((int)ini_size + 1);
9898 char* const buf = g.SettingsIniData.Buf.Data;
9899 char* const buf_end = buf + ini_size;
9900 memcpy(buf, ini_data, ini_size);
9901 buf_end[0] = 0;
9902
9903 // Call pre-read handlers
9904 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
9905 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9906 if (g.SettingsHandlers[handler_n].ReadInitFn)
9907 g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]);
9908
9909 void* entry_data = NULL;
9910 ImGuiSettingsHandler* entry_handler = NULL;
9911
9912 char* line_end = NULL;
9913 for (char* line = buf; line < buf_end; line = line_end + 1)
9914 {
9915 // Skip new lines markers, then find end of the line
9916 while (*line == '\n' || *line == '\r')
9917 line++;
9918 line_end = line;
9919 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9920 line_end++;
9921 line_end[0] = 0;
9922 if (line[0] == ';')
9923 continue;
9924 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9925 {
9926 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9927 line_end[-1] = 0;
9928 const char* name_end = line_end - 1;
9929 const char* type_start = line + 1;
9930 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
9931 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9932 if (!type_end || !name_start)
9933 continue;
9934 *type_end = 0; // Overwrite first ']'
9935 name_start++; // Skip second '['
9936 entry_handler = FindSettingsHandler(type_start);
9937 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9938 }
9939 else if (entry_handler != NULL && entry_data != NULL)
9940 {
9941 // Let type handler parse the line
9942 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9943 }
9944 }
9945 g.SettingsLoaded = true;
9946
9947 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
9948 memcpy(buf, ini_data, ini_size);
9949
9950 // Call post-read handlers
9951 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9952 if (g.SettingsHandlers[handler_n].ApplyAllFn)
9953 g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]);
9954 }
9955
SaveIniSettingsToDisk(const char * ini_filename)9956 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9957 {
9958 ImGuiContext& g = *GImGui;
9959 g.SettingsDirtyTimer = 0.0f;
9960 if (!ini_filename)
9961 return;
9962
9963 size_t ini_data_size = 0;
9964 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9965 ImFileHandle f = ImFileOpen(ini_filename, "wt");
9966 if (!f)
9967 return;
9968 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
9969 ImFileClose(f);
9970 }
9971
9972 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9973 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9974 {
9975 ImGuiContext& g = *GImGui;
9976 g.SettingsDirtyTimer = 0.0f;
9977 g.SettingsIniData.Buf.resize(0);
9978 g.SettingsIniData.Buf.push_back(0);
9979 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9980 {
9981 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9982 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9983 }
9984 if (out_size)
9985 *out_size = (size_t)g.SettingsIniData.size();
9986 return g.SettingsIniData.c_str();
9987 }
9988
WindowSettingsHandler_ClearAll(ImGuiContext * ctx,ImGuiSettingsHandler *)9989 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
9990 {
9991 ImGuiContext& g = *ctx;
9992 for (int i = 0; i != g.Windows.Size; i++)
9993 g.Windows[i]->SettingsOffset = -1;
9994 g.SettingsWindows.clear();
9995 }
9996
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9997 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9998 {
9999 ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name);
10000 ImGuiID id = settings->ID;
10001 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
10002 settings->ID = id;
10003 settings->WantApply = true;
10004 return (void*)settings;
10005 }
10006
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)10007 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
10008 {
10009 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
10010 int x, y;
10011 int i;
10012 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); }
10013 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); }
10014 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
10015 }
10016
10017 // Apply to existing windows (if any)
WindowSettingsHandler_ApplyAll(ImGuiContext * ctx,ImGuiSettingsHandler *)10018 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
10019 {
10020 ImGuiContext& g = *ctx;
10021 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10022 if (settings->WantApply)
10023 {
10024 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
10025 ApplyWindowSettings(window, settings);
10026 settings->WantApply = false;
10027 }
10028 }
10029
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)10030 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
10031 {
10032 // Gather data from windows that were active during this session
10033 // (if a window wasn't opened in this session we preserve its settings)
10034 ImGuiContext& g = *ctx;
10035 for (int i = 0; i != g.Windows.Size; i++)
10036 {
10037 ImGuiWindow* window = g.Windows[i];
10038 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
10039 continue;
10040
10041 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
10042 if (!settings)
10043 {
10044 settings = ImGui::CreateNewWindowSettings(window->Name);
10045 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
10046 }
10047 IM_ASSERT(settings->ID == window->ID);
10048 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
10049 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
10050 settings->Collapsed = window->Collapsed;
10051 }
10052
10053 // Write to text buffer
10054 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
10055 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10056 {
10057 const char* settings_name = settings->GetName();
10058 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
10059 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
10060 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
10061 buf->appendf("Collapsed=%d\n", settings->Collapsed);
10062 buf->append("\n");
10063 }
10064 }
10065
10066
10067 //-----------------------------------------------------------------------------
10068 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
10069 //-----------------------------------------------------------------------------
10070
10071 // (this section is filled in the 'docking' branch)
10072
10073
10074 //-----------------------------------------------------------------------------
10075 // [SECTION] DOCKING
10076 //-----------------------------------------------------------------------------
10077
10078 // (this section is filled in the 'docking' branch)
10079
10080
10081 //-----------------------------------------------------------------------------
10082 // [SECTION] PLATFORM DEPENDENT HELPERS
10083 //-----------------------------------------------------------------------------
10084
10085 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
10086
10087 #ifdef _MSC_VER
10088 #pragma comment(lib, "user32")
10089 #pragma comment(lib, "kernel32")
10090 #endif
10091
10092 // Win32 clipboard implementation
10093 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
GetClipboardTextFn_DefaultImpl(void *)10094 static const char* GetClipboardTextFn_DefaultImpl(void*)
10095 {
10096 ImGuiContext& g = *GImGui;
10097 g.ClipboardHandlerData.clear();
10098 if (!::OpenClipboard(NULL))
10099 return NULL;
10100 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
10101 if (wbuf_handle == NULL)
10102 {
10103 ::CloseClipboard();
10104 return NULL;
10105 }
10106 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
10107 {
10108 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
10109 g.ClipboardHandlerData.resize(buf_len);
10110 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
10111 }
10112 ::GlobalUnlock(wbuf_handle);
10113 ::CloseClipboard();
10114 return g.ClipboardHandlerData.Data;
10115 }
10116
SetClipboardTextFn_DefaultImpl(void *,const char * text)10117 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10118 {
10119 if (!::OpenClipboard(NULL))
10120 return;
10121 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
10122 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
10123 if (wbuf_handle == NULL)
10124 {
10125 ::CloseClipboard();
10126 return;
10127 }
10128 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
10129 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
10130 ::GlobalUnlock(wbuf_handle);
10131 ::EmptyClipboard();
10132 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
10133 ::GlobalFree(wbuf_handle);
10134 ::CloseClipboard();
10135 }
10136
10137 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
10138
10139 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
10140 static PasteboardRef main_clipboard = 0;
10141
10142 // OSX clipboard implementation
10143 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)10144 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10145 {
10146 if (!main_clipboard)
10147 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10148 PasteboardClear(main_clipboard);
10149 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
10150 if (cf_data)
10151 {
10152 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
10153 CFRelease(cf_data);
10154 }
10155 }
10156
GetClipboardTextFn_DefaultImpl(void *)10157 static const char* GetClipboardTextFn_DefaultImpl(void*)
10158 {
10159 if (!main_clipboard)
10160 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
10161 PasteboardSynchronize(main_clipboard);
10162
10163 ItemCount item_count = 0;
10164 PasteboardGetItemCount(main_clipboard, &item_count);
10165 for (ItemCount i = 0; i < item_count; i++)
10166 {
10167 PasteboardItemID item_id = 0;
10168 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
10169 CFArrayRef flavor_type_array = 0;
10170 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
10171 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
10172 {
10173 CFDataRef cf_data;
10174 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
10175 {
10176 ImGuiContext& g = *GImGui;
10177 g.ClipboardHandlerData.clear();
10178 int length = (int)CFDataGetLength(cf_data);
10179 g.ClipboardHandlerData.resize(length + 1);
10180 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
10181 g.ClipboardHandlerData[length] = 0;
10182 CFRelease(cf_data);
10183 return g.ClipboardHandlerData.Data;
10184 }
10185 }
10186 }
10187 return NULL;
10188 }
10189
10190 #else
10191
10192 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)10193 static const char* GetClipboardTextFn_DefaultImpl(void*)
10194 {
10195 ImGuiContext& g = *GImGui;
10196 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
10197 }
10198
SetClipboardTextFn_DefaultImpl(void *,const char * text)10199 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
10200 {
10201 ImGuiContext& g = *GImGui;
10202 g.ClipboardHandlerData.clear();
10203 const char* text_end = text + strlen(text);
10204 g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
10205 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
10206 g.ClipboardHandlerData[(int)(text_end - text)] = 0;
10207 }
10208
10209 #endif
10210
10211 // Win32 API IME support (for Asian languages, etc.)
10212 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
10213
10214 #include <imm.h>
10215 #ifdef _MSC_VER
10216 #pragma comment(lib, "imm32")
10217 #endif
10218
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)10219 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
10220 {
10221 // Notify OS Input Method Editor of text input position
10222 ImGuiIO& io = ImGui::GetIO();
10223 if (HWND hwnd = (HWND)io.ImeWindowHandle)
10224 if (HIMC himc = ::ImmGetContext(hwnd))
10225 {
10226 COMPOSITIONFORM cf;
10227 cf.ptCurrentPos.x = x;
10228 cf.ptCurrentPos.y = y;
10229 cf.dwStyle = CFS_FORCE_POSITION;
10230 ::ImmSetCompositionWindow(himc, &cf);
10231 ::ImmReleaseContext(hwnd, himc);
10232 }
10233 }
10234
10235 #else
10236
ImeSetInputScreenPosFn_DefaultImpl(int,int)10237 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
10238
10239 #endif
10240
10241 //-----------------------------------------------------------------------------
10242 // [SECTION] METRICS/DEBUG WINDOW
10243 //-----------------------------------------------------------------------------
10244
10245 #ifndef IMGUI_DISABLE_METRICS_WINDOW
10246 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)10247 static void MetricsHelpMarker(const char* desc)
10248 {
10249 ImGui::TextDisabled("(?)");
10250 if (ImGui::IsItemHovered())
10251 {
10252 ImGui::BeginTooltip();
10253 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
10254 ImGui::TextUnformatted(desc);
10255 ImGui::PopTextWrapPos();
10256 ImGui::EndTooltip();
10257 }
10258 }
10259
ShowMetricsWindow(bool * p_open)10260 void ImGui::ShowMetricsWindow(bool* p_open)
10261 {
10262 if (!ImGui::Begin("Dear ImGui Metrics", p_open))
10263 {
10264 ImGui::End();
10265 return;
10266 }
10267
10268 // Debugging enums
10269 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
10270 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
10271 enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type
10272 const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" };
10273
10274 // State
10275 static bool show_windows_rects = false;
10276 static int show_windows_rect_type = WRT_WorkRect;
10277 static bool show_windows_begin_order = false;
10278 static bool show_tables_rects = false;
10279 static int show_tables_rect_type = TRT_WorkRect;
10280 static bool show_drawcmd_mesh = true;
10281 static bool show_drawcmd_aabb = true;
10282
10283 // Basic info
10284 ImGuiContext& g = *GImGui;
10285 ImGuiIO& io = ImGui::GetIO();
10286 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
10287 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
10288 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
10289 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
10290 ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
10291 ImGui::Separator();
10292
10293 // Helper functions to display common structures:
10294 // - NodeDrawList()
10295 // - NodeColumns()
10296 // - NodeWindow()
10297 // - NodeWindows()
10298 // - NodeTabBar()
10299 // - NodeStorage()
10300 struct Funcs
10301 {
10302 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
10303 {
10304 if (rect_type == WRT_OuterRect) { return window->Rect(); }
10305 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
10306 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
10307 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
10308 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
10309 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
10310 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
10311 IM_ASSERT(0);
10312 return ImRect();
10313 }
10314
10315 static void NodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb)
10316 {
10317 IM_ASSERT(show_mesh || show_aabb);
10318 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
10319 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10320
10321 // Draw wire-frame version of all triangles
10322 ImRect clip_rect = draw_cmd->ClipRect;
10323 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
10324 ImDrawListFlags backup_flags = fg_draw_list->Flags;
10325 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10326 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + draw_cmd->ElemCount); base_idx += 3)
10327 {
10328 ImVec2 triangle[3];
10329 for (int n = 0; n < 3; n++)
10330 {
10331 ImVec2 p = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
10332 triangle[n] = p;
10333 vtxs_rect.Add(p);
10334 }
10335 if (show_mesh)
10336 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles
10337 }
10338 // Draw bounding boxes
10339 if (show_aabb)
10340 {
10341 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
10342 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
10343 }
10344 fg_draw_list->Flags = backup_flags;
10345 }
10346
10347 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
10348 {
10349 bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size);
10350 if (draw_list == ImGui::GetWindowDrawList())
10351 {
10352 ImGui::SameLine();
10353 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
10354 if (node_open) ImGui::TreePop();
10355 return;
10356 }
10357
10358 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
10359 if (window && IsItemHovered())
10360 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
10361 if (!node_open)
10362 return;
10363
10364 if (window && !window->WasActive)
10365 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
10366
10367 unsigned int elem_offset = 0;
10368 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
10369 {
10370 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
10371 continue;
10372 if (pcmd->UserCallback)
10373 {
10374 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
10375 continue;
10376 }
10377
10378 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
10379 char buf[300];
10380 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
10381 pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,
10382 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
10383 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
10384 if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list)
10385 NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb);
10386 if (!pcmd_node_open)
10387 continue;
10388
10389 // Calculate approximate coverage area (touched pixel count)
10390 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
10391 float total_area = 0.0f;
10392 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
10393 {
10394 ImVec2 triangle[3];
10395 for (int n = 0; n < 3; n++)
10396 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
10397 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
10398 }
10399
10400 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
10401 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
10402 ImGui::Selectable(buf);
10403 if (ImGui::IsItemHovered() && fg_draw_list)
10404 NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false);
10405
10406 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
10407 ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10408 while (clipper.Step())
10409 for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
10410 {
10411 char* buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
10412 ImVec2 triangle[3];
10413 for (int n = 0; n < 3; n++, idx_i++)
10414 {
10415 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
10416 triangle[n] = v.pos;
10417 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
10418 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10419 }
10420
10421 ImGui::Selectable(buf, false);
10422 if (fg_draw_list && ImGui::IsItemHovered())
10423 {
10424 ImDrawListFlags backup_flags = fg_draw_list->Flags;
10425 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10426 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f);
10427 fg_draw_list->Flags = backup_flags;
10428 }
10429 }
10430 ImGui::TreePop();
10431 }
10432 ImGui::TreePop();
10433 }
10434
10435 static void NodeColumns(const ImGuiColumns* columns)
10436 {
10437 if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
10438 return;
10439 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
10440 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
10441 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
10442 ImGui::TreePop();
10443 }
10444
10445 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
10446 {
10447 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
10448 return;
10449 ImGui::Text("(In front-to-back order:)");
10450 for (int i = windows.Size - 1; i >= 0; i--) // Iterate front to back
10451 {
10452 ImGui::PushID(windows[i]);
10453 Funcs::NodeWindow(windows[i], "Window");
10454 ImGui::PopID();
10455 }
10456 ImGui::TreePop();
10457 }
10458
10459 static void NodeWindow(ImGuiWindow* window, const char* label)
10460 {
10461 if (window == NULL)
10462 {
10463 ImGui::BulletText("%s: NULL", label);
10464 return;
10465 }
10466
10467 ImGuiContext& g = *GImGui;
10468 const bool is_active = window->WasActive;
10469 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
10470 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
10471 const bool open = ImGui::TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
10472 if (!is_active) { PopStyleColor(); }
10473 if (ImGui::IsItemHovered() && is_active)
10474 ImGui::GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
10475 if (!open)
10476 return;
10477
10478 if (window->MemoryCompacted)
10479 ImGui::TextDisabled("Note: some memory buffers have been compacted/freed.");
10480
10481 ImGuiWindowFlags flags = window->Flags;
10482 NodeDrawList(window, window->DrawList, "DrawList");
10483 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y);
10484 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
10485 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
10486 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
10487 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
10488 ImGui::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" : "");
10489 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
10490 ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
10491 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
10492 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
10493 if (!window->NavRectRel[0].IsInverted())
10494 ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
10495 else
10496 ImGui::BulletText("NavRectRel[0]: <None>");
10497 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
10498 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
10499 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
10500 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
10501 {
10502 for (int n = 0; n < window->ColumnsStorage.Size; n++)
10503 NodeColumns(&window->ColumnsStorage[n]);
10504 ImGui::TreePop();
10505 }
10506 NodeStorage(&window->StateStorage, "Storage");
10507 ImGui::TreePop();
10508 }
10509
10510 static void NodeWindowSettings(ImGuiWindowSettings* settings)
10511 {
10512 ImGui::Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
10513 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
10514 }
10515
10516 static void NodeTabBar(ImGuiTabBar* tab_bar)
10517 {
10518 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
10519 char buf[256];
10520 char* p = buf;
10521 const char* buf_end = buf + IM_ARRAYSIZE(buf);
10522 const bool is_active = (tab_bar->PrevFrameVisible >= ImGui::GetFrameCount() - 2);
10523 p += ImFormatString(p, buf_end - p, "Tab Bar 0x%08X (%d tabs)%s", tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
10524 IM_UNUSED(p);
10525 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
10526 bool open = ImGui::TreeNode(tab_bar, "%s", buf);
10527 if (!is_active) { PopStyleColor(); }
10528 if (open)
10529 {
10530 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
10531 {
10532 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
10533 ImGui::PushID(tab);
10534 if (ImGui::SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
10535 if (ImGui::SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } ImGui::SameLine();
10536 ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "");
10537 ImGui::PopID();
10538 }
10539 ImGui::TreePop();
10540 }
10541 }
10542
10543 static void NodeStorage(ImGuiStorage* storage, const char* label)
10544 {
10545 if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
10546 return;
10547 for (int n = 0; n < storage->Data.Size; n++)
10548 {
10549 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
10550 ImGui::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.
10551 }
10552 ImGui::TreePop();
10553 }
10554 };
10555
10556 // Tools
10557 if (ImGui::TreeNode("Tools"))
10558 {
10559 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10560 if (ImGui::Button("Item Picker.."))
10561 ImGui::DebugStartItemPicker();
10562 ImGui::SameLine();
10563 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.");
10564
10565 ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
10566 ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
10567 ImGui::SameLine();
10568 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
10569 show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count);
10570 if (show_windows_rects && g.NavWindow)
10571 {
10572 ImGui::BulletText("'%s':", g.NavWindow->Name);
10573 ImGui::Indent();
10574 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10575 {
10576 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10577 ImGui::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]);
10578 }
10579 ImGui::Unindent();
10580 }
10581 ImGui::Checkbox("Show mesh when hovering ImDrawCmd", &show_drawcmd_mesh);
10582 ImGui::Checkbox("Show bounding boxes when hovering ImDrawCmd", &show_drawcmd_aabb);
10583 ImGui::TreePop();
10584 }
10585
10586 // Contents
10587 Funcs::NodeWindows(g.Windows, "Windows");
10588 //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder");
10589 if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
10590 {
10591 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
10592 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
10593 ImGui::TreePop();
10594 }
10595
10596 // Details for Popups
10597 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
10598 {
10599 for (int i = 0; i < g.OpenPopupStack.Size; i++)
10600 {
10601 ImGuiWindow* window = g.OpenPopupStack[i].Window;
10602 ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : "");
10603 }
10604 ImGui::TreePop();
10605 }
10606
10607 // Details for TabBars
10608 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
10609 {
10610 for (int n = 0; n < g.TabBars.GetSize(); n++)
10611 Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
10612 ImGui::TreePop();
10613 }
10614
10615 // Details for Tables
10616 IM_UNUSED(trt_rects_names);
10617 IM_UNUSED(show_tables_rects);
10618 IM_UNUSED(show_tables_rect_type);
10619 #ifdef IMGUI_HAS_TABLE
10620 if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
10621 {
10622 for (int n = 0; n < g.Tables.GetSize(); n++)
10623 Funcs::NodeTable(g.Tables.GetByIndex(n));
10624 ImGui::TreePop();
10625 }
10626 #endif // #ifdef IMGUI_HAS_TABLE
10627
10628 // Details for Docking
10629 #ifdef IMGUI_HAS_DOCK
10630 if (ImGui::TreeNode("Dock nodes"))
10631 {
10632 ImGui::TreePop();
10633 }
10634 #endif // #ifdef IMGUI_HAS_DOCK
10635
10636 // Settings
10637 if (ImGui::TreeNode("Settings"))
10638 {
10639 if (ImGui::SmallButton("Clear"))
10640 ImGui::ClearIniSettings();
10641 ImGui::SameLine();
10642 if (ImGui::SmallButton("Save to memory"))
10643 ImGui::SaveIniSettingsToMemory();
10644 ImGui::SameLine();
10645 if (ImGui::SmallButton("Save to disk"))
10646 ImGui::SaveIniSettingsToDisk(g.IO.IniFilename);
10647 ImGui::SameLine();
10648 if (g.IO.IniFilename)
10649 ImGui::Text("\"%s\"", g.IO.IniFilename);
10650 else
10651 ImGui::TextUnformatted("<NULL>");
10652 ImGui::Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
10653 if (ImGui::TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
10654 {
10655 for (int n = 0; n < g.SettingsHandlers.Size; n++)
10656 ImGui::BulletText("%s", g.SettingsHandlers[n].TypeName);
10657 ImGui::TreePop();
10658 }
10659 if (ImGui::TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
10660 {
10661 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
10662 Funcs::NodeWindowSettings(settings);
10663 ImGui::TreePop();
10664 }
10665
10666 #ifdef IMGUI_HAS_TABLE
10667 if (ImGui::TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
10668 {
10669 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
10670 Funcs::NodeTableSettings(settings);
10671 ImGui::TreePop();
10672 }
10673 #endif // #ifdef IMGUI_HAS_TABLE
10674
10675 #ifdef IMGUI_HAS_DOCK
10676 #endif // #ifdef IMGUI_HAS_DOCK
10677
10678 if (ImGui::TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
10679 {
10680 ImGui::InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, 0.0f), ImGuiInputTextFlags_ReadOnly);
10681 ImGui::TreePop();
10682 }
10683 ImGui::TreePop();
10684 }
10685
10686 // Misc Details
10687 if (ImGui::TreeNode("Internal state"))
10688 {
10689 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10690
10691 ImGui::Text("WINDOWING");
10692 ImGui::Indent();
10693 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10694 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10695 ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
10696 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10697 ImGui::Unindent();
10698
10699 ImGui::Text("ITEMS");
10700 ImGui::Indent();
10701 ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
10702 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10703 ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
10704 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10705 ImGui::Unindent();
10706
10707 ImGui::Text("NAV,FOCUS");
10708 ImGui::Indent();
10709 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10710 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10711 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10712 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10713 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10714 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10715 ImGui::Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
10716 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10717 ImGui::Unindent();
10718
10719 ImGui::TreePop();
10720 }
10721
10722 // Overlay: Display windows Rectangles and Begin Order
10723 if (show_windows_rects || show_windows_begin_order)
10724 {
10725 for (int n = 0; n < g.Windows.Size; n++)
10726 {
10727 ImGuiWindow* window = g.Windows[n];
10728 if (!window->WasActive)
10729 continue;
10730 ImDrawList* draw_list = GetForegroundDrawList(window);
10731 if (show_windows_rects)
10732 {
10733 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
10734 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10735 }
10736 if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10737 {
10738 char buf[32];
10739 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10740 float font_size = ImGui::GetFontSize();
10741 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10742 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10743 }
10744 }
10745 }
10746
10747 #ifdef IMGUI_HAS_TABLE
10748 // Overlay: Display Tables Rectangles
10749 if (show_tables_rects)
10750 {
10751 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10752 {
10753 ImGuiTable* table = g.Tables.GetByIndex(table_n);
10754 }
10755 }
10756 #endif // #ifdef IMGUI_HAS_TABLE
10757
10758 #ifdef IMGUI_HAS_DOCK
10759 // Overlay: Display Docking info
10760 if (show_docking_nodes && g.IO.KeyCtrl)
10761 {
10762 }
10763 #endif // #ifdef IMGUI_HAS_DOCK
10764
10765 ImGui::End();
10766 }
10767
10768 #else
10769
ShowMetricsWindow(bool *)10770 void ImGui::ShowMetricsWindow(bool*) { }
10771
10772 #endif
10773
10774 //-----------------------------------------------------------------------------
10775
10776 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
10777 // 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.
10778 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
10779 #include "imgui_user.inl"
10780 #endif
10781
10782 //-----------------------------------------------------------------------------
10783
10784 #endif // #ifndef IMGUI_DISABLE
10785