1 // dear imgui, v1.75
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 for demo code. All applications in examples/ are doing that.
8
9 // Resources:
10 // - FAQ http://dearimgui.org/faq
11 // - Homepage & latest https://github.com/ocornut/imgui
12 // - Releases & changelog https://github.com/ocornut/imgui/releases
13 // - Gallery https://github.com/ocornut/imgui/issues/2847 (please post your screenshots/video there!)
14 // - Glossary https://github.com/ocornut/imgui/wiki/Glossary
15 // - Wiki https://github.com/ocornut/imgui/wiki
16 // - Issues & support https://github.com/ocornut/imgui/issues
17
18 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
19 // See LICENSE.txt for copyright and licensing details (standard MIT License).
20 // This library is free but I need your support to sustain development and maintenance.
21 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org".
22 // Individuals: you can support continued development via donations. See docs/README or web page.
23
24 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
25 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
26 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
27 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
28 // to a better solution or official support for them.
29
30 /*
31
32 Index of this file:
33
34 DOCUMENTATION
35
36 - MISSION STATEMENT
37 - END-USER GUIDE
38 - PROGRAMMER GUIDE
39 - READ FIRST
40 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
41 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
42 - HOW A SIMPLE APPLICATION MAY LOOK LIKE (2 variations)
43 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
44 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
45 - API BREAKING CHANGES (read me when you update!)
46 - FREQUENTLY ASKED QUESTIONS (FAQ)
47 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
48
49 CODE
50 (search for "[SECTION]" in the code to find them)
51
52 // [SECTION] FORWARD DECLARATIONS
53 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
54 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
55 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
56 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
57 // [SECTION] MISC HELPERS/UTILITIES (File functions)
58 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
59 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
60 // [SECTION] ImGuiStorage
61 // [SECTION] ImGuiTextFilter
62 // [SECTION] ImGuiTextBuffer
63 // [SECTION] ImGuiListClipper
64 // [SECTION] RENDER HELPERS
65 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
66 // [SECTION] ERROR CHECKING
67 // [SECTION] SCROLLING
68 // [SECTION] TOOLTIPS
69 // [SECTION] POPUPS
70 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
71 // [SECTION] DRAG AND DROP
72 // [SECTION] LOGGING/CAPTURING
73 // [SECTION] SETTINGS
74 // [SECTION] PLATFORM DEPENDENT HELPERS
75 // [SECTION] METRICS/DEBUG WINDOW
76
77 */
78
79 //-----------------------------------------------------------------------------
80 // DOCUMENTATION
81 //-----------------------------------------------------------------------------
82
83 /*
84
85 MISSION STATEMENT
86 =================
87
88 - Easy to use to create code-driven and data-driven tools.
89 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
90 - Easy to hack and improve.
91 - Minimize screen real-estate usage.
92 - Minimize setup and maintenance.
93 - Minimize state storage on user side.
94 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
95 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
96 opening a tree node for the first time, etc. but a typical frame should not allocate anything).
97
98 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
99 - Doesn't look fancy, doesn't animate.
100 - Limited layout features, intricate layouts are typically crafted in code.
101
102
103 END-USER GUIDE
104 ==============
105
106 - Double-click on title bar to collapse window.
107 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
108 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
109 - Click and drag on any empty space to move window.
110 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
111 - CTRL+Click on a slider or drag box to input value as text.
112 - Use mouse wheel to scroll.
113 - Text editor:
114 - Hold SHIFT or use mouse to select text.
115 - CTRL+Left/Right to word jump.
116 - CTRL+Shift+Left/Right to select words.
117 - CTRL+A our Double-Click to select all.
118 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
119 - CTRL+Z,CTRL+Y to undo/redo.
120 - ESCAPE to revert text to its original value.
121 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
122 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
123 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
124 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
125
126
127 PROGRAMMER GUIDE
128 ================
129
130 READ FIRST
131 ----------
132 - Remember to read the FAQ (https://www.dearimgui.org/faq)
133 - 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
134 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
135 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
136 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
137 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
138 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
139 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
140 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,
141 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
142 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
143 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
144 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
145 If you get an assert, read the messages and comments around the assert.
146 - 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.
147 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
148 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
149 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
150 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
151
152 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
153 ----------------------------------------------
154 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
155 - Or maintain your own branch where you have imconfig.h modified.
156 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
157 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
158 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
159 likely be a comment about it. Please report any issue to the GitHub page!
160 - Try to keep your copy of dear imgui reasonably up to date.
161
162 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
163 ---------------------------------------------------------------
164 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
165 - In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder.
166 - Add the Dear ImGui source files to your projects or using your preferred build system.
167 It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL).
168 - 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.
169 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
170 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
171 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
172 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
173 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
174 - 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.
175
176 HOW A SIMPLE APPLICATION MAY LOOK LIKE
177 --------------------------------------
178 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
179
180 // Application init: create a dear imgui context, setup some options, load fonts
181 ImGui::CreateContext();
182 ImGuiIO& io = ImGui::GetIO();
183 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
184 // TODO: Fill optional fields of the io structure later.
185 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
186
187 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
188 ImGui_ImplWin32_Init(hwnd);
189 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
190
191 // Application main loop
192 while (true)
193 {
194 // Feed inputs to dear imgui, start new frame
195 ImGui_ImplDX11_NewFrame();
196 ImGui_ImplWin32_NewFrame();
197 ImGui::NewFrame();
198
199 // Any application code here
200 ImGui::Text("Hello, world!");
201
202 // Render dear imgui into screen
203 ImGui::Render();
204 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
205 g_pSwapChain->Present(1, 0);
206 }
207
208 // Shutdown
209 ImGui_ImplDX11_Shutdown();
210 ImGui_ImplWin32_Shutdown();
211 ImGui::DestroyContext();
212
213 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
214
215 // Application init: create a dear imgui context, setup some options, load fonts
216 ImGui::CreateContext();
217 ImGuiIO& io = ImGui::GetIO();
218 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
219 // TODO: Fill optional fields of the io structure later.
220 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
221
222 // Build and load the texture atlas into a texture
223 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
224 int width, height;
225 unsigned char* pixels = NULL;
226 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
227
228 // At this point you've got the texture data and you need to upload that your your graphic system:
229 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
230 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
231 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
232 io.Fonts->TexID = (void*)texture;
233
234 // Application main loop
235 while (true)
236 {
237 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
238 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
239 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
240 io.DisplaySize.x = 1920.0f; // set the current display width
241 io.DisplaySize.y = 1280.0f; // set the current display height here
242 io.MousePos = my_mouse_pos; // set the mouse position
243 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
244 io.MouseDown[1] = my_mouse_buttons[1];
245
246 // Call NewFrame(), after this point you can use ImGui::* functions anytime
247 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
248 ImGui::NewFrame();
249
250 // Most of your application code here
251 ImGui::Text("Hello, world!");
252 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
253 MyGameRender(); // may use any Dear ImGui functions as well!
254
255 // Render dear imgui, swap buffers
256 // (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)
257 ImGui::EndFrame();
258 ImGui::Render();
259 ImDrawData* draw_data = ImGui::GetDrawData();
260 MyImGuiRenderFunction(draw_data);
261 SwapBuffers();
262 }
263
264 // Shutdown
265 ImGui::DestroyContext();
266
267 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
268 ---------------------------------------------
269 void void MyImGuiRenderFunction(ImDrawData* draw_data)
270 {
271 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
272 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
273 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
274 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
275 for (int n = 0; n < draw_data->CmdListsCount; n++)
276 {
277 const ImDrawList* cmd_list = draw_data->CmdLists[n];
278 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
279 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
280 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
281 {
282 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
283 if (pcmd->UserCallback)
284 {
285 pcmd->UserCallback(cmd_list, pcmd);
286 }
287 else
288 {
289 // The texture for the draw call is specified by pcmd->TextureId.
290 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
291 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
292
293 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
294 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
295 // (some elements visible outside their bounds) but you can fix that once everything else works!
296 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
297 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
298 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
299 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
300 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
301 ImVec2 pos = draw_data->DisplayPos;
302 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));
303
304 // Render 'pcmd->ElemCount/3' indexed triangles.
305 // 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.
306 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
307 }
308 idx_buffer += pcmd->ElemCount;
309 }
310 }
311 }
312
313 - The examples/ folders contains many actual implementation of the pseudo-codes above.
314 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
315 They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the
316 rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
317 - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
318
319 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
320 ------------------------------------------
321 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
322 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse!
323 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
324 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
325 - Keyboard:
326 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
327 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
328 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
329 will be set. For more advanced uses, you may want to read from:
330 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
331 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
332 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
333 Please reach out if you think the game vs navigation input sharing could be improved.
334 - Gamepad:
335 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
336 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
337 Note that io.NavInputs[] is cleared by EndFrame().
338 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
339 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
340 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
341 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.).
342 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
343 - 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
344 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
345 - Mouse:
346 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
347 - 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.
348 - 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.
349 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
350 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.
351 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.
352 (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!)
353 (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
354 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
355
356
357 API BREAKING CHANGES
358 ====================
359
360 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
361 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.
362 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.
363 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
364
365 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
366 - 2019/12/17 (1.75) - 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.
367 - 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.
368 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
369 - ShowTestWindow() -> use ShowDemoWindow()
370 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
371 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
372 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f)
373 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing()
374 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg
375 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding
376 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
377 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS
378 - 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.
379 - 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).
380 - 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.
381 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
382 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
383 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
384 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
385 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
386 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding()
387 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
388 - ImFont::Glyph -> use ImFontGlyph
389 - 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.
390 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.
391 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).
392 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
393 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
394 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
395 - 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.
396 - 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
397 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.
398 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.
399 Please reach out if you are affected.
400 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
401 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
402 - 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.
403 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
404 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
405 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
406 - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value!
407 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
408 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
409 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
410 - 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.
411 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
412 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
413 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
414 - 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.
415 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
416 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
417 - 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.
418 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
419 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
420 - 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).
421 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
422 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
423 - 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.
424 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
425 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
426 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
427 - 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.).
428 old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
429 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
430 in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
431 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
432 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
433 - 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.
434 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
435 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.
436 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.
437 - 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",
438 consistent with other functions. Kept redirection functions (will obsolete).
439 - 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.
440 - 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).
441 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
442 - 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.
443 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
444 - 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.
445 - 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.
446 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
447 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
448 - removed Shutdown() function, as DestroyContext() serve this purpose.
449 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
450 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
451 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
452 - 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.
453 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
454 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
455 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
456 - 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.
457 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
458 - 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
459 - 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.
460 - 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.
461 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
462 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
463 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
464 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
465 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
466 - 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.
467 - 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.
468 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.
469 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
470 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
471 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
472 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
473 - 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.
474 - 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.
475 - 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.
476 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
477 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
478 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
479 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
480 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
481 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
482 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
483 - 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).
484 - 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)".
485 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
486 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
487 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
488 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
489 - 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.
490 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
491 - 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.
492 - 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).
493 - 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).
494 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
495 - 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.
496 - 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.
497 - 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))'
498 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
499 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
500 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
501 - 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().
502 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
503 - 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.
504 - 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.
505 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
506 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.
507 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:
508 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); }
509 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.
510 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
511 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
512 - 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).
513 - 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.
514 - 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).
515 - 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)
516 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
517 - 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.
518 - 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.
519 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
520 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
521 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
522 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.
523 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!
524 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
525 - 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.
526 - 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
527 - 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.
528 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
529 - 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.
530 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
531 - 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.
532 - the signature of the io.RenderDrawListsFn handler has changed!
533 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
534 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
535 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
536 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
537 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
538 - 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.
539 - 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!
540 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
541 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
542 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
543 - 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.
544 - 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
545 - 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!
546 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
547 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
548 - 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.
549 - 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.
550 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
551 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
552 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
553 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
554 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
555 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
556 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
557 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
558 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
559 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
560 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
561 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
562 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
563 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
564 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
565 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
566 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
567 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
568 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
569 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier;
570 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.
571 - 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.
572 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
573 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
574 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
575 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
576 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
577 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
578 - 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)
579 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
580 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
581 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
582 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
583 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
584 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
585
586
587 FREQUENTLY ASKED QUESTIONS (FAQ)
588 ================================
589
590 Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
591 Some answers are copied down here to facilitate searching in code.
592
593 Q&A: Basics
594 ===========
595
596 Q: Where is the documentation?
597 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
598 - Run the examples/ and explore them.
599 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
600 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
601 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
602 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the
603 examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
604 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
605 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
606 - Your programming IDE is your friend, find the type or function declaration to find comments
607 associated to it.
608
609 Q: What is this library called?
610 Q: Which version should I get?
611 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
612 >> See https://www.dearimgui.org/faq
613
614 Q&A: Integration
615 ================
616
617 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
618 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
619 >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this.
620
621 Q. How can I enable keyboard controls?
622 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
623 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
624 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
625 >> See https://www.dearimgui.org/faq
626
627 Q&A: Usage
628 ----------
629
630 Q: Why are multiple widgets reacting when I interact with a single one?
631 Q: How can I have multiple widgets with the same label or with an empty label?
632 A: A primer on labels and the ID Stack...
633
634 Dear ImGui internally need to uniquely identify UI elements.
635 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
636 Interactive widgets (such as calls to Button buttons) need a unique ID.
637 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
638 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
639
640 - Unique ID are often derived from a string label:
641
642 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
643 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
644
645 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
646 two buttons labeled "OK" in different windows or different tree locations is fine.
647 We used "..." above to signify whatever was already pushed to the ID stack previously:
648
649 Begin("MyWindow");
650 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
651 End();
652 Begin("MyOtherWindow");
653 Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
654 End();
655
656 - If you have a same ID twice in the same location, you'll have a conflict:
657
658 Button("OK");
659 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
660
661 Fear not! this is easy to solve and there are many ways to solve it!
662
663 - Solving ID conflict in a simple/local context:
664 When passing a label you can optionally specify extra ID information within string itself.
665 Use "##" to pass a complement to the ID that won't be visible to the end-user.
666 This helps solving the simple collision cases when you know e.g. at compilation time which items
667 are going to be created:
668
669 Begin("MyWindow");
670 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
671 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
672 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
673 End();
674
675 - If you want to completely hide the label, but still need an ID:
676
677 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
678
679 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
680 you to animate labels. For example you may want to include varying information in a window title bar,
681 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
682
683 Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID")
684 Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different
685
686 sprintf(buf, "My game (%f FPS)###MyGame", fps);
687 Begin(buf); // Variable title, ID = hash of "MyGame"
688
689 - Solving ID conflict in a more general manner:
690 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
691 within the same window. This is the most convenient way of distinguishing ID when iterating and
692 creating many UI elements programmatically.
693 You can push a pointer, a string or an integer value into the ID stack.
694 Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
695 At each level of the stack we store the seed used for items at this level of the ID stack.
696
697 Begin("Window");
698 for (int i = 0; i < 100; i++)
699 {
700 PushID(i); // Push i to the id tack
701 Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click")
702 PopID();
703 }
704 for (int i = 0; i < 100; i++)
705 {
706 MyObject* obj = Objects[i];
707 PushID(obj);
708 Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click")
709 PopID();
710 }
711 for (int i = 0; i < 100; i++)
712 {
713 MyObject* obj = Objects[i];
714 PushID(obj->Name);
715 Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click")
716 PopID();
717 }
718 End();
719
720 - You can stack multiple prefixes into the ID stack:
721
722 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
723 PushID("node");
724 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
725 PushID(my_ptr);
726 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
727 PopID();
728 PopID();
729
730 - Tree nodes implicitly creates a scope for you by calling PushID().
731
732 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
733 if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
734 {
735 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
736 TreePop();
737 }
738
739 - When working with trees, ID are used to preserve the open/close state of each tree node.
740 Depending on your use cases you may want to use strings, indices or pointers as ID.
741 e.g. when following a single pointer that may change over time, using a static string as ID
742 will preserve your node open/closed state when the targeted object change.
743 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
744 node open/closed state differently. See what makes more sense in your situation!
745
746 Q: How can I display an image? What is ImTextureID, how does it works?
747 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
748
749 Q: How can I use my own math types instead of ImVec2/ImVec4?
750 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
751 Q: How can I display custom shapes? (using low-level ImDrawList API)
752 >> See https://www.dearimgui.org/faq
753
754 Q&A: Fonts, Text
755 ================
756
757 Q: How can I load a different font than the default?
758 Q: How can I easily use icons in my application?
759 Q: How can I load multiple fonts?
760 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
761 >> See https://www.dearimgui.org/faq and docs/FONTS.txt
762
763 Q&A: Concerns
764 =============
765
766 Q: Who uses Dear ImGui?
767 Q: Can you create elaborate/serious tools with Dear ImGui?
768 Q: Can you reskin the look of Dear ImGui?
769 Q: Why using C++ (as opposed to C)?
770 >> See https://www.dearimgui.org/faq
771
772 Q&A: Community
773 ==============
774
775 Q: How can I help?
776 A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui!
777 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
778 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.
779 - Individuals: you can support continued development via PayPal donations. See README.
780 - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
781 and see how you want to help and can help!
782 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
783 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/2847). Visuals are ideal as they inspire other programmers.
784 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
785 - 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).
786
787 */
788
789 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
790 #define _CRT_SECURE_NO_WARNINGS
791 #endif
792
793 #include "imgui.h"
794 #ifndef IMGUI_DISABLE
795
796 #ifndef IMGUI_DEFINE_MATH_OPERATORS
797 #define IMGUI_DEFINE_MATH_OPERATORS
798 #endif
799 #include "imgui_internal.h"
800
801 #include <ctype.h> // toupper
802 #include <stdio.h> // vsnprintf, sscanf, printf
803 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
804 #include <stddef.h> // intptr_t
805 #else
806 #include <stdint.h> // intptr_t
807 #endif
808
809 // Debug options
810 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
811 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
812 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower)
813
814 // Visual Studio warnings
815 #ifdef _MSC_VER
816 #pragma warning (disable: 4127) // condition expression is constant
817 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
818 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
819 #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
820 #endif
821 #endif
822
823 // Clang/GCC warnings with -Weverything
824 #if defined(__clang__)
825 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great!
826 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
827 #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.
828 #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.
829 #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.
830 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is.
831 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
832 #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.
833 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
834 #if __has_warning("-Wzero-as-null-pointer-constant")
835 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
836 #endif
837 #if __has_warning("-Wdouble-promotion")
838 #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.
839 #endif
840 #elif defined(__GNUC__)
841 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
842 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
843 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
844 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
845 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
846 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
847 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
848 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
849 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
850 #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
851 #endif
852
853 // 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.
854 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
855 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
856
857 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
858 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
859 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
860 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 certaint time, unless mouse moved.
861
862 //-------------------------------------------------------------------------
863 // [SECTION] FORWARD DECLARATIONS
864 //-------------------------------------------------------------------------
865
866 static void SetCurrentWindow(ImGuiWindow* window);
867 static void FindHoveredWindow();
868 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
869 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
870
871 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
872 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
873
874 static ImRect GetViewportRect();
875
876 // Settings
877 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
878 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
879 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
880
881 // Platform Dependents default implementation for IO functions
882 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
883 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
884 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
885
886 namespace ImGui
887 {
888 // Navigation
889 static void NavUpdate();
890 static void NavUpdateWindowing();
891 static void NavUpdateWindowingOverlay();
892 static void NavUpdateMoveResult();
893 static float NavUpdatePageUpPageDown();
894 static inline void NavUpdateAnyRequestFlag();
895 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
896 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
897 static ImVec2 NavCalcPreferredRefPos();
898 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
899 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
900 static int FindWindowFocusIndex(ImGuiWindow* window);
901
902 // Error Checking
903 static void ErrorCheckEndFrame();
904 static void ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write);
905
906 // Misc
907 static void UpdateMouseInputs();
908 static void UpdateMouseWheel();
909 static bool UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
910 static void UpdateDebugToolItemPicker();
911 static void RenderWindowOuterBorders(ImGuiWindow* window);
912 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);
913 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
914
915 }
916
917 //-----------------------------------------------------------------------------
918 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
919 //-----------------------------------------------------------------------------
920
921 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
922 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
923 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
924 // SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
925 // In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
926 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
927 // If you want thread-safety to allow N threads to access N different contexts, you can:
928 // - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
929 // struct ImGuiContext;
930 // extern thread_local ImGuiContext* MyImGuiTLS;
931 // #define GImGui MyImGuiTLS
932 // 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.
933 // - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
934 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
935 #ifndef GImGui
936 ImGuiContext* GImGui = NULL;
937 #endif
938
939 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
940 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
941 // 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.
942 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)943 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)944 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
945 #else
MallocWrapper(size_t size,void * user_data)946 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)947 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
948 #endif
949
950 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
951 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
952 static void* GImAllocatorUserData = NULL;
953
954 //-----------------------------------------------------------------------------
955 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
956 //-----------------------------------------------------------------------------
957
ImGuiStyle()958 ImGuiStyle::ImGuiStyle()
959 {
960 Alpha = 1.0f; // Global alpha applies to everything in ImGui
961 WindowPadding = ImVec2(8,8); // Padding within a window
962 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
963 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
964 WindowMinSize = ImVec2(32,32); // Minimum window size
965 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
966 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
967 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
968 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
969 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
970 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
971 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
972 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
973 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
974 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
975 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
976 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!
977 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
978 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
979 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
980 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
981 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
982 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
983 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
984 TabBorderSize = 0.0f; // Thickness of border around tabs.
985 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
986 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
987 SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
988 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.
989 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.
990 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
991 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
992 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
993 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.
994 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.
995
996 // Default theme
997 ImGui::StyleColorsDark(this);
998 }
999
1000 // 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.
1001 // 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)1002 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1003 {
1004 WindowPadding = ImFloor(WindowPadding * scale_factor);
1005 WindowRounding = ImFloor(WindowRounding * scale_factor);
1006 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1007 ChildRounding = ImFloor(ChildRounding * scale_factor);
1008 PopupRounding = ImFloor(PopupRounding * scale_factor);
1009 FramePadding = ImFloor(FramePadding * scale_factor);
1010 FrameRounding = ImFloor(FrameRounding * scale_factor);
1011 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1012 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1013 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1014 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1015 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1016 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1017 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1018 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1019 GrabRounding = ImFloor(GrabRounding * scale_factor);
1020 TabRounding = ImFloor(TabRounding * scale_factor);
1021 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1022 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1023 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1024 }
1025
ImGuiIO()1026 ImGuiIO::ImGuiIO()
1027 {
1028 // Most fields are initialized with zero
1029 memset(this, 0, sizeof(*this));
1030 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.
1031
1032 // Settings
1033 ConfigFlags = ImGuiConfigFlags_None;
1034 BackendFlags = ImGuiBackendFlags_None;
1035 DisplaySize = ImVec2(-1.0f, -1.0f);
1036 DeltaTime = 1.0f/60.0f;
1037 IniSavingRate = 5.0f;
1038 IniFilename = "imgui.ini";
1039 LogFilename = "imgui_log.txt";
1040 MouseDoubleClickTime = 0.30f;
1041 MouseDoubleClickMaxDist = 6.0f;
1042 for (int i = 0; i < ImGuiKey_COUNT; i++)
1043 KeyMap[i] = -1;
1044 KeyRepeatDelay = 0.275f;
1045 KeyRepeatRate = 0.050f;
1046 UserData = NULL;
1047
1048 Fonts = NULL;
1049 FontGlobalScale = 1.0f;
1050 FontDefault = NULL;
1051 FontAllowUserScaling = false;
1052 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1053
1054 // Miscellaneous options
1055 MouseDrawCursor = false;
1056 #ifdef __APPLE__
1057 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1058 #else
1059 ConfigMacOSXBehaviors = false;
1060 #endif
1061 ConfigInputTextCursorBlink = true;
1062 ConfigWindowsResizeFromEdges = true;
1063 ConfigWindowsMoveFromTitleBarOnly = false;
1064 ConfigWindowsMemoryCompactTimer = 60.0f;
1065
1066 // Platform Functions
1067 BackendPlatformName = BackendRendererName = NULL;
1068 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1069 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1070 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1071 ClipboardUserData = NULL;
1072 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1073 ImeWindowHandle = NULL;
1074
1075 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1076 RenderDrawListsFn = NULL;
1077 #endif
1078
1079 // Input (NB: we already have memset zero the entire structure!)
1080 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1081 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1082 MouseDragThreshold = 6.0f;
1083 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1084 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1085 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1086 }
1087
1088 // Pass in translated ASCII characters for text input.
1089 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1090 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1091 void ImGuiIO::AddInputCharacter(unsigned int c)
1092 {
1093 if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1094 InputQueueCharacters.push_back((ImWchar)c);
1095 }
1096
AddInputCharactersUTF8(const char * utf8_chars)1097 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1098 {
1099 while (*utf8_chars != 0)
1100 {
1101 unsigned int c = 0;
1102 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1103 if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1104 InputQueueCharacters.push_back((ImWchar)c);
1105 }
1106 }
1107
ClearInputCharacters()1108 void ImGuiIO::ClearInputCharacters()
1109 {
1110 InputQueueCharacters.resize(0);
1111 }
1112
1113 //-----------------------------------------------------------------------------
1114 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1115 //-----------------------------------------------------------------------------
1116
ImBezierClosestPoint(const ImVec2 & p1,const ImVec2 & p2,const ImVec2 & p3,const ImVec2 & p4,const ImVec2 & p,int num_segments)1117 ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1118 {
1119 IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau()
1120 ImVec2 p_last = p1;
1121 ImVec2 p_closest;
1122 float p_closest_dist2 = FLT_MAX;
1123 float t_step = 1.0f / (float)num_segments;
1124 for (int i_step = 1; i_step <= num_segments; i_step++)
1125 {
1126 ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step);
1127 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1128 float dist2 = ImLengthSqr(p - p_line);
1129 if (dist2 < p_closest_dist2)
1130 {
1131 p_closest = p_line;
1132 p_closest_dist2 = dist2;
1133 }
1134 p_last = p_current;
1135 }
1136 return p_closest;
1137 }
1138
1139 // 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)1140 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)
1141 {
1142 float dx = x4 - x1;
1143 float dy = y4 - y1;
1144 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1145 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1146 d2 = (d2 >= 0) ? d2 : -d2;
1147 d3 = (d3 >= 0) ? d3 : -d3;
1148 if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy))
1149 {
1150 ImVec2 p_current(x4, y4);
1151 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1152 float dist2 = ImLengthSqr(p - p_line);
1153 if (dist2 < p_closest_dist2)
1154 {
1155 p_closest = p_line;
1156 p_closest_dist2 = dist2;
1157 }
1158 p_last = p_current;
1159 }
1160 else if (level < 10)
1161 {
1162 float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f;
1163 float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f;
1164 float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f;
1165 float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f;
1166 float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f;
1167 float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f;
1168 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1169 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1170 }
1171 }
1172
1173 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1174 // 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)1175 ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1176 {
1177 IM_ASSERT(tess_tol > 0.0f);
1178 ImVec2 p_last = p1;
1179 ImVec2 p_closest;
1180 float p_closest_dist2 = FLT_MAX;
1181 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);
1182 return p_closest;
1183 }
1184
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1185 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1186 {
1187 ImVec2 ap = p - a;
1188 ImVec2 ab_dir = b - a;
1189 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1190 if (dot < 0.0f)
1191 return a;
1192 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1193 if (dot > ab_len_sqr)
1194 return b;
1195 return a + ab_dir * dot / ab_len_sqr;
1196 }
1197
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1198 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1199 {
1200 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1201 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1202 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1203 return ((b1 == b2) && (b2 == b3));
1204 }
1205
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1206 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1207 {
1208 ImVec2 v0 = b - a;
1209 ImVec2 v1 = c - a;
1210 ImVec2 v2 = p - a;
1211 const float denom = v0.x * v1.y - v1.x * v0.y;
1212 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1213 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1214 out_u = 1.0f - out_v - out_w;
1215 }
1216
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1217 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1218 {
1219 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1220 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1221 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1222 float dist2_ab = ImLengthSqr(p - proj_ab);
1223 float dist2_bc = ImLengthSqr(p - proj_bc);
1224 float dist2_ca = ImLengthSqr(p - proj_ca);
1225 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1226 if (m == dist2_ab)
1227 return proj_ab;
1228 if (m == dist2_bc)
1229 return proj_bc;
1230 return proj_ca;
1231 }
1232
1233 //-----------------------------------------------------------------------------
1234 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
1235 //-----------------------------------------------------------------------------
1236
1237 // 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)1238 int ImStricmp(const char* str1, const char* str2)
1239 {
1240 int d;
1241 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1242 return d;
1243 }
1244
ImStrnicmp(const char * str1,const char * str2,size_t count)1245 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1246 {
1247 int d = 0;
1248 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1249 return d;
1250 }
1251
ImStrncpy(char * dst,const char * src,size_t count)1252 void ImStrncpy(char* dst, const char* src, size_t count)
1253 {
1254 if (count < 1)
1255 return;
1256 if (count > 1)
1257 strncpy(dst, src, count - 1);
1258 dst[count - 1] = 0;
1259 }
1260
ImStrdup(const char * str)1261 char* ImStrdup(const char* str)
1262 {
1263 size_t len = strlen(str);
1264 void* buf = IM_ALLOC(len + 1);
1265 return (char*)memcpy(buf, (const void*)str, len + 1);
1266 }
1267
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1268 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1269 {
1270 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1271 size_t src_size = strlen(src) + 1;
1272 if (dst_buf_size < src_size)
1273 {
1274 IM_FREE(dst);
1275 dst = (char*)IM_ALLOC(src_size);
1276 if (p_dst_size)
1277 *p_dst_size = src_size;
1278 }
1279 return (char*)memcpy(dst, (const void*)src, src_size);
1280 }
1281
ImStrchrRange(const char * str,const char * str_end,char c)1282 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1283 {
1284 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1285 return p;
1286 }
1287
ImStrlenW(const ImWchar * str)1288 int ImStrlenW(const ImWchar* str)
1289 {
1290 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1291 int n = 0;
1292 while (*str++) n++;
1293 return n;
1294 }
1295
1296 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1297 const char* ImStreolRange(const char* str, const char* str_end)
1298 {
1299 const char* p = (const char*)memchr(str, '\n', str_end - str);
1300 return p ? p : str_end;
1301 }
1302
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1303 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1304 {
1305 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1306 buf_mid_line--;
1307 return buf_mid_line;
1308 }
1309
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1310 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1311 {
1312 if (!needle_end)
1313 needle_end = needle + strlen(needle);
1314
1315 const char un0 = (char)toupper(*needle);
1316 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1317 {
1318 if (toupper(*haystack) == un0)
1319 {
1320 const char* b = needle + 1;
1321 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1322 if (toupper(*a) != toupper(*b))
1323 break;
1324 if (b == needle_end)
1325 return haystack;
1326 }
1327 haystack++;
1328 }
1329 return NULL;
1330 }
1331
1332 // 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)1333 void ImStrTrimBlanks(char* buf)
1334 {
1335 char* p = buf;
1336 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1337 p++;
1338 char* p_start = p;
1339 while (*p != 0) // Find end of string
1340 p++;
1341 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1342 p--;
1343 if (p_start != buf) // Copy memory if we had leading blanks
1344 memmove(buf, p_start, p - p_start);
1345 buf[p - p_start] = 0; // Zero terminate
1346 }
1347
ImStrSkipBlank(const char * str)1348 const char* ImStrSkipBlank(const char* str)
1349 {
1350 while (str[0] == ' ' || str[0] == '\t')
1351 str++;
1352 return str;
1353 }
1354
1355 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1356 // 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.
1357 // B) When buf==NULL vsnprintf() will return the output size.
1358 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1359
1360 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1361 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1362 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1363 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1364 #ifdef IMGUI_USE_STB_SPRINTF
1365 #define STB_SPRINTF_IMPLEMENTATION
1366 #include "stb_sprintf.h"
1367 #endif
1368
1369 #if defined(_MSC_VER) && !defined(vsnprintf)
1370 #define vsnprintf _vsnprintf
1371 #endif
1372
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1373 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1374 {
1375 va_list args;
1376 va_start(args, fmt);
1377 #ifdef IMGUI_USE_STB_SPRINTF
1378 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1379 #else
1380 int w = vsnprintf(buf, buf_size, fmt, args);
1381 #endif
1382 va_end(args);
1383 if (buf == NULL)
1384 return w;
1385 if (w == -1 || w >= (int)buf_size)
1386 w = (int)buf_size - 1;
1387 buf[w] = 0;
1388 return w;
1389 }
1390
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1391 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1392 {
1393 #ifdef IMGUI_USE_STB_SPRINTF
1394 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1395 #else
1396 int w = vsnprintf(buf, buf_size, fmt, args);
1397 #endif
1398 if (buf == NULL)
1399 return w;
1400 if (w == -1 || w >= (int)buf_size)
1401 w = (int)buf_size - 1;
1402 buf[w] = 0;
1403 return w;
1404 }
1405 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1406
1407 // CRC32 needs a 1KB lookup table (not cache friendly)
1408 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1409 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1410 static const ImU32 GCrc32LookupTable[256] =
1411 {
1412 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1413 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1414 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1415 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1416 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1417 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1418 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1419 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1420 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1421 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1422 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1423 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1424 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1425 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1426 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1427 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1428 };
1429
1430 // Known size hash
1431 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1432 // 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)1433 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1434 {
1435 ImU32 crc = ~seed;
1436 const unsigned char* data = (const unsigned char*)data_p;
1437 const ImU32* crc32_lut = GCrc32LookupTable;
1438 while (data_size-- != 0)
1439 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1440 return ~crc;
1441 }
1442
1443 // Zero-terminated string hash, with support for ### to reset back to seed value
1444 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1445 // Because this syntax is rarely used we are optimizing for the common case.
1446 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1447 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1448 // 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)1449 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1450 {
1451 seed = ~seed;
1452 ImU32 crc = seed;
1453 const unsigned char* data = (const unsigned char*)data_p;
1454 const ImU32* crc32_lut = GCrc32LookupTable;
1455 if (data_size != 0)
1456 {
1457 while (data_size-- != 0)
1458 {
1459 unsigned char c = *data++;
1460 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1461 crc = seed;
1462 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1463 }
1464 }
1465 else
1466 {
1467 while (unsigned char c = *data++)
1468 {
1469 if (c == '#' && data[0] == '#' && data[1] == '#')
1470 crc = seed;
1471 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1472 }
1473 }
1474 return ~crc;
1475 }
1476
1477 //-----------------------------------------------------------------------------
1478 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1479 //-----------------------------------------------------------------------------
1480
1481 // Default file functions
1482 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
ImFileOpen(const char * filename,const char * mode)1483 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1484 {
1485 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1486 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1487 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1488 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1489 ImVector<ImWchar> buf;
1490 buf.resize(filename_wsize + mode_wsize);
1491 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1492 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1493 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1494 #else
1495 return fopen(filename, mode);
1496 #endif
1497 }
1498
1499 // 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)1500 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1501 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)1502 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)1503 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
1504 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1505
1506 // Helper: Load file content into memory
1507 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * mode,size_t * out_file_size,int padding_bytes)1508 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1509 {
1510 IM_ASSERT(filename && mode);
1511 if (out_file_size)
1512 *out_file_size = 0;
1513
1514 ImFileHandle f;
1515 if ((f = ImFileOpen(filename, mode)) == NULL)
1516 return NULL;
1517
1518 size_t file_size = (size_t)ImFileGetSize(f);
1519 if (file_size == (size_t)-1)
1520 {
1521 ImFileClose(f);
1522 return NULL;
1523 }
1524
1525 void* file_data = IM_ALLOC(file_size + padding_bytes);
1526 if (file_data == NULL)
1527 {
1528 ImFileClose(f);
1529 return NULL;
1530 }
1531 if (ImFileRead(file_data, 1, file_size, f) != file_size)
1532 {
1533 ImFileClose(f);
1534 IM_FREE(file_data);
1535 return NULL;
1536 }
1537 if (padding_bytes > 0)
1538 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1539
1540 ImFileClose(f);
1541 if (out_file_size)
1542 *out_file_size = file_size;
1543
1544 return file_data;
1545 }
1546
1547 //-----------------------------------------------------------------------------
1548 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1549 //-----------------------------------------------------------------------------
1550
1551 // Convert UTF-8 to 32-bit character, process single character input.
1552 // Based on stb_from_utf8() from github.com/nothings/stb/
1553 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1554 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1555 {
1556 unsigned int c = (unsigned int)-1;
1557 const unsigned char* str = (const unsigned char*)in_text;
1558 if (!(*str & 0x80))
1559 {
1560 c = (unsigned int)(*str++);
1561 *out_char = c;
1562 return 1;
1563 }
1564 if ((*str & 0xe0) == 0xc0)
1565 {
1566 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1567 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1568 if (*str < 0xc2) return 2;
1569 c = (unsigned int)((*str++ & 0x1f) << 6);
1570 if ((*str & 0xc0) != 0x80) return 2;
1571 c += (*str++ & 0x3f);
1572 *out_char = c;
1573 return 2;
1574 }
1575 if ((*str & 0xf0) == 0xe0)
1576 {
1577 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1578 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1579 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1580 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1581 c = (unsigned int)((*str++ & 0x0f) << 12);
1582 if ((*str & 0xc0) != 0x80) return 3;
1583 c += (unsigned int)((*str++ & 0x3f) << 6);
1584 if ((*str & 0xc0) != 0x80) return 3;
1585 c += (*str++ & 0x3f);
1586 *out_char = c;
1587 return 3;
1588 }
1589 if ((*str & 0xf8) == 0xf0)
1590 {
1591 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1592 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1593 if (*str > 0xf4) return 4;
1594 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1595 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1596 c = (unsigned int)((*str++ & 0x07) << 18);
1597 if ((*str & 0xc0) != 0x80) return 4;
1598 c += (unsigned int)((*str++ & 0x3f) << 12);
1599 if ((*str & 0xc0) != 0x80) return 4;
1600 c += (unsigned int)((*str++ & 0x3f) << 6);
1601 if ((*str & 0xc0) != 0x80) return 4;
1602 c += (*str++ & 0x3f);
1603 // utf-8 encodings of values used in surrogate pairs are invalid
1604 if ((c & 0xFFFFF800) == 0xD800) return 4;
1605 *out_char = c;
1606 return 4;
1607 }
1608 *out_char = 0;
1609 return 0;
1610 }
1611
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1612 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1613 {
1614 ImWchar* buf_out = buf;
1615 ImWchar* buf_end = buf + buf_size;
1616 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1617 {
1618 unsigned int c;
1619 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1620 if (c == 0)
1621 break;
1622 if (c <= IM_UNICODE_CODEPOINT_MAX) // FIXME: Losing characters that don't fit in 2 bytes
1623 *buf_out++ = (ImWchar)c;
1624 }
1625 *buf_out = 0;
1626 if (in_text_remaining)
1627 *in_text_remaining = in_text;
1628 return (int)(buf_out - buf);
1629 }
1630
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1631 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1632 {
1633 int char_count = 0;
1634 while ((!in_text_end || in_text < in_text_end) && *in_text)
1635 {
1636 unsigned int c;
1637 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1638 if (c == 0)
1639 break;
1640 if (c <= IM_UNICODE_CODEPOINT_MAX)
1641 char_count++;
1642 }
1643 return char_count;
1644 }
1645
1646 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1647 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1648 {
1649 if (c < 0x80)
1650 {
1651 buf[0] = (char)c;
1652 return 1;
1653 }
1654 if (c < 0x800)
1655 {
1656 if (buf_size < 2) return 0;
1657 buf[0] = (char)(0xc0 + (c >> 6));
1658 buf[1] = (char)(0x80 + (c & 0x3f));
1659 return 2;
1660 }
1661 if (c >= 0xdc00 && c < 0xe000)
1662 {
1663 return 0;
1664 }
1665 if (c >= 0xd800 && c < 0xdc00)
1666 {
1667 if (buf_size < 4) return 0;
1668 buf[0] = (char)(0xf0 + (c >> 18));
1669 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1670 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1671 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1672 return 4;
1673 }
1674 //else if (c < 0x10000)
1675 {
1676 if (buf_size < 3) return 0;
1677 buf[0] = (char)(0xe0 + (c >> 12));
1678 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1679 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1680 return 3;
1681 }
1682 }
1683
1684 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1685 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1686 {
1687 unsigned int dummy = 0;
1688 return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1689 }
1690
ImTextCountUtf8BytesFromChar(unsigned int c)1691 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1692 {
1693 if (c < 0x80) return 1;
1694 if (c < 0x800) return 2;
1695 if (c >= 0xdc00 && c < 0xe000) return 0;
1696 if (c >= 0xd800 && c < 0xdc00) return 4;
1697 return 3;
1698 }
1699
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1700 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1701 {
1702 char* buf_out = buf;
1703 const char* buf_end = buf + buf_size;
1704 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1705 {
1706 unsigned int c = (unsigned int)(*in_text++);
1707 if (c < 0x80)
1708 *buf_out++ = (char)c;
1709 else
1710 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1711 }
1712 *buf_out = 0;
1713 return (int)(buf_out - buf);
1714 }
1715
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1716 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1717 {
1718 int bytes_count = 0;
1719 while ((!in_text_end || in_text < in_text_end) && *in_text)
1720 {
1721 unsigned int c = (unsigned int)(*in_text++);
1722 if (c < 0x80)
1723 bytes_count++;
1724 else
1725 bytes_count += ImTextCountUtf8BytesFromChar(c);
1726 }
1727 return bytes_count;
1728 }
1729
1730 //-----------------------------------------------------------------------------
1731 // [SECTION] MISC HELPERS/UTILTIES (Color functions)
1732 // Note: The Convert functions are early design which are not consistent with other API.
1733 //-----------------------------------------------------------------------------
1734
ColorConvertU32ToFloat4(ImU32 in)1735 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1736 {
1737 float s = 1.0f/255.0f;
1738 return ImVec4(
1739 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1740 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1741 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1742 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1743 }
1744
ColorConvertFloat4ToU32(const ImVec4 & in)1745 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1746 {
1747 ImU32 out;
1748 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1749 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1750 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1751 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1752 return out;
1753 }
1754
1755 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1756 // 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)1757 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1758 {
1759 float K = 0.f;
1760 if (g < b)
1761 {
1762 ImSwap(g, b);
1763 K = -1.f;
1764 }
1765 if (r < g)
1766 {
1767 ImSwap(r, g);
1768 K = -2.f / 6.f - K;
1769 }
1770
1771 const float chroma = r - (g < b ? g : b);
1772 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1773 out_s = chroma / (r + 1e-20f);
1774 out_v = r;
1775 }
1776
1777 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1778 // 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)1779 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1780 {
1781 if (s == 0.0f)
1782 {
1783 // gray
1784 out_r = out_g = out_b = v;
1785 return;
1786 }
1787
1788 h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1789 int i = (int)h;
1790 float f = h - (float)i;
1791 float p = v * (1.0f - s);
1792 float q = v * (1.0f - s * f);
1793 float t = v * (1.0f - s * (1.0f - f));
1794
1795 switch (i)
1796 {
1797 case 0: out_r = v; out_g = t; out_b = p; break;
1798 case 1: out_r = q; out_g = v; out_b = p; break;
1799 case 2: out_r = p; out_g = v; out_b = t; break;
1800 case 3: out_r = p; out_g = q; out_b = v; break;
1801 case 4: out_r = t; out_g = p; out_b = v; break;
1802 case 5: default: out_r = v; out_g = p; out_b = q; break;
1803 }
1804 }
1805
1806 //-----------------------------------------------------------------------------
1807 // [SECTION] ImGuiStorage
1808 // Helper: Key->value storage
1809 //-----------------------------------------------------------------------------
1810
1811 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1812 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1813 {
1814 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1815 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1816 size_t count = (size_t)(last - first);
1817 while (count > 0)
1818 {
1819 size_t count2 = count >> 1;
1820 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1821 if (mid->key < key)
1822 {
1823 first = ++mid;
1824 count -= count2 + 1;
1825 }
1826 else
1827 {
1828 count = count2;
1829 }
1830 }
1831 return first;
1832 }
1833
1834 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1835 void ImGuiStorage::BuildSortByKey()
1836 {
1837 struct StaticFunc
1838 {
1839 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1840 {
1841 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1842 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1843 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1844 return 0;
1845 }
1846 };
1847 if (Data.Size > 1)
1848 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1849 }
1850
GetInt(ImGuiID key,int default_val) const1851 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1852 {
1853 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1854 if (it == Data.end() || it->key != key)
1855 return default_val;
1856 return it->val_i;
1857 }
1858
GetBool(ImGuiID key,bool default_val) const1859 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1860 {
1861 return GetInt(key, default_val ? 1 : 0) != 0;
1862 }
1863
GetFloat(ImGuiID key,float default_val) const1864 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1865 {
1866 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1867 if (it == Data.end() || it->key != key)
1868 return default_val;
1869 return it->val_f;
1870 }
1871
GetVoidPtr(ImGuiID key) const1872 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1873 {
1874 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1875 if (it == Data.end() || it->key != key)
1876 return NULL;
1877 return it->val_p;
1878 }
1879
1880 // 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)1881 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1882 {
1883 ImGuiStoragePair* it = LowerBound(Data, key);
1884 if (it == Data.end() || it->key != key)
1885 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1886 return &it->val_i;
1887 }
1888
GetBoolRef(ImGuiID key,bool default_val)1889 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1890 {
1891 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1892 }
1893
GetFloatRef(ImGuiID key,float default_val)1894 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1895 {
1896 ImGuiStoragePair* it = LowerBound(Data, key);
1897 if (it == Data.end() || it->key != key)
1898 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1899 return &it->val_f;
1900 }
1901
GetVoidPtrRef(ImGuiID key,void * default_val)1902 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1903 {
1904 ImGuiStoragePair* it = LowerBound(Data, key);
1905 if (it == Data.end() || it->key != key)
1906 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1907 return &it->val_p;
1908 }
1909
1910 // 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)1911 void ImGuiStorage::SetInt(ImGuiID key, int val)
1912 {
1913 ImGuiStoragePair* it = LowerBound(Data, key);
1914 if (it == Data.end() || it->key != key)
1915 {
1916 Data.insert(it, ImGuiStoragePair(key, val));
1917 return;
1918 }
1919 it->val_i = val;
1920 }
1921
SetBool(ImGuiID key,bool val)1922 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1923 {
1924 SetInt(key, val ? 1 : 0);
1925 }
1926
SetFloat(ImGuiID key,float val)1927 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1928 {
1929 ImGuiStoragePair* it = LowerBound(Data, key);
1930 if (it == Data.end() || it->key != key)
1931 {
1932 Data.insert(it, ImGuiStoragePair(key, val));
1933 return;
1934 }
1935 it->val_f = val;
1936 }
1937
SetVoidPtr(ImGuiID key,void * val)1938 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1939 {
1940 ImGuiStoragePair* it = LowerBound(Data, key);
1941 if (it == Data.end() || it->key != key)
1942 {
1943 Data.insert(it, ImGuiStoragePair(key, val));
1944 return;
1945 }
1946 it->val_p = val;
1947 }
1948
SetAllInt(int v)1949 void ImGuiStorage::SetAllInt(int v)
1950 {
1951 for (int i = 0; i < Data.Size; i++)
1952 Data[i].val_i = v;
1953 }
1954
1955 //-----------------------------------------------------------------------------
1956 // [SECTION] ImGuiTextFilter
1957 //-----------------------------------------------------------------------------
1958
1959 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1960 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1961 {
1962 if (default_filter)
1963 {
1964 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1965 Build();
1966 }
1967 else
1968 {
1969 InputBuf[0] = 0;
1970 CountGrep = 0;
1971 }
1972 }
1973
Draw(const char * label,float width)1974 bool ImGuiTextFilter::Draw(const char* label, float width)
1975 {
1976 if (width != 0.0f)
1977 ImGui::SetNextItemWidth(width);
1978 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1979 if (value_changed)
1980 Build();
1981 return value_changed;
1982 }
1983
split(char separator,ImVector<ImGuiTextRange> * out) const1984 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1985 {
1986 out->resize(0);
1987 const char* wb = b;
1988 const char* we = wb;
1989 while (we < e)
1990 {
1991 if (*we == separator)
1992 {
1993 out->push_back(ImGuiTextRange(wb, we));
1994 wb = we + 1;
1995 }
1996 we++;
1997 }
1998 if (wb != we)
1999 out->push_back(ImGuiTextRange(wb, we));
2000 }
2001
Build()2002 void ImGuiTextFilter::Build()
2003 {
2004 Filters.resize(0);
2005 ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
2006 input_range.split(',', &Filters);
2007
2008 CountGrep = 0;
2009 for (int i = 0; i != Filters.Size; i++)
2010 {
2011 ImGuiTextRange& f = Filters[i];
2012 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2013 f.b++;
2014 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2015 f.e--;
2016 if (f.empty())
2017 continue;
2018 if (Filters[i].b[0] != '-')
2019 CountGrep += 1;
2020 }
2021 }
2022
PassFilter(const char * text,const char * text_end) const2023 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2024 {
2025 if (Filters.empty())
2026 return true;
2027
2028 if (text == NULL)
2029 text = "";
2030
2031 for (int i = 0; i != Filters.Size; i++)
2032 {
2033 const ImGuiTextRange& f = Filters[i];
2034 if (f.empty())
2035 continue;
2036 if (f.b[0] == '-')
2037 {
2038 // Subtract
2039 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2040 return false;
2041 }
2042 else
2043 {
2044 // Grep
2045 if (ImStristr(text, text_end, f.b, f.e) != NULL)
2046 return true;
2047 }
2048 }
2049
2050 // Implicit * grep
2051 if (CountGrep == 0)
2052 return true;
2053
2054 return false;
2055 }
2056
2057 //-----------------------------------------------------------------------------
2058 // [SECTION] ImGuiTextBuffer
2059 //-----------------------------------------------------------------------------
2060
2061 // On some platform vsnprintf() takes va_list by reference and modifies it.
2062 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2063 #ifndef va_copy
2064 #if defined(__GNUC__) || defined(__clang__)
2065 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2066 #else
2067 #define va_copy(dest, src) (dest = src)
2068 #endif
2069 #endif
2070
2071 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2072
append(const char * str,const char * str_end)2073 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2074 {
2075 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2076
2077 // Add zero-terminator the first time
2078 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2079 const int needed_sz = write_off + len;
2080 if (write_off + len >= Buf.Capacity)
2081 {
2082 int new_capacity = Buf.Capacity * 2;
2083 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2084 }
2085
2086 Buf.resize(needed_sz);
2087 memcpy(&Buf[write_off - 1], str, (size_t)len);
2088 Buf[write_off - 1 + len] = 0;
2089 }
2090
appendf(const char * fmt,...)2091 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2092 {
2093 va_list args;
2094 va_start(args, fmt);
2095 appendfv(fmt, args);
2096 va_end(args);
2097 }
2098
2099 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2100 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2101 {
2102 va_list args_copy;
2103 va_copy(args_copy, args);
2104
2105 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2106 if (len <= 0)
2107 {
2108 va_end(args_copy);
2109 return;
2110 }
2111
2112 // Add zero-terminator the first time
2113 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2114 const int needed_sz = write_off + len;
2115 if (write_off + len >= Buf.Capacity)
2116 {
2117 int new_capacity = Buf.Capacity * 2;
2118 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2119 }
2120
2121 Buf.resize(needed_sz);
2122 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2123 va_end(args_copy);
2124 }
2125
2126 //-----------------------------------------------------------------------------
2127 // [SECTION] ImGuiListClipper
2128 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2129 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2130 //-----------------------------------------------------------------------------
2131
2132 // Helper to calculate coarse clipping of large list of evenly sized items.
2133 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2134 // 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)2135 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2136 {
2137 ImGuiContext& g = *GImGui;
2138 ImGuiWindow* window = g.CurrentWindow;
2139 if (g.LogEnabled)
2140 {
2141 // If logging is active, do not perform any clipping
2142 *out_items_display_start = 0;
2143 *out_items_display_end = items_count;
2144 return;
2145 }
2146 if (window->SkipItems)
2147 {
2148 *out_items_display_start = *out_items_display_end = 0;
2149 return;
2150 }
2151
2152 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2153 ImRect unclipped_rect = window->ClipRect;
2154 if (g.NavMoveRequest)
2155 unclipped_rect.Add(g.NavScoringRectScreen);
2156
2157 const ImVec2 pos = window->DC.CursorPos;
2158 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2159 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2160
2161 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2162 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2163 start--;
2164 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2165 end++;
2166
2167 start = ImClamp(start, 0, items_count);
2168 end = ImClamp(end + 1, start, items_count);
2169 *out_items_display_start = start;
2170 *out_items_display_end = end;
2171 }
2172
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2173 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2174 {
2175 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2176 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2177 // The clipper should probably have a 4th step to display the last item in a regular manner.
2178 ImGuiContext& g = *GImGui;
2179 ImGuiWindow* window = g.CurrentWindow;
2180 window->DC.CursorPos.y = pos_y;
2181 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2182 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.
2183 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.
2184 if (ImGuiColumns* columns = window->DC.CurrentColumns)
2185 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2186 }
2187
2188 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2189 // Use case B: Begin() called from constructor with items_height>0
2190 // 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)2191 void ImGuiListClipper::Begin(int count, float items_height)
2192 {
2193 ImGuiContext& g = *GImGui;
2194 ImGuiWindow* window = g.CurrentWindow;
2195
2196 StartPosY = window->DC.CursorPos.y;
2197 ItemsHeight = items_height;
2198 ItemsCount = count;
2199 StepNo = 0;
2200 DisplayEnd = DisplayStart = -1;
2201 if (ItemsHeight > 0.0f)
2202 {
2203 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2204 if (DisplayStart > 0)
2205 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2206 StepNo = 2;
2207 }
2208 }
2209
End()2210 void ImGuiListClipper::End()
2211 {
2212 if (ItemsCount < 0)
2213 return;
2214 // 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.
2215 if (ItemsCount < INT_MAX)
2216 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2217 ItemsCount = -1;
2218 StepNo = 3;
2219 }
2220
Step()2221 bool ImGuiListClipper::Step()
2222 {
2223 ImGuiContext& g = *GImGui;
2224 ImGuiWindow* window = g.CurrentWindow;
2225
2226 if (ItemsCount == 0 || window->SkipItems)
2227 {
2228 ItemsCount = -1;
2229 return false;
2230 }
2231 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.
2232 {
2233 DisplayStart = 0;
2234 DisplayEnd = 1;
2235 StartPosY = window->DC.CursorPos.y;
2236 StepNo = 1;
2237 return true;
2238 }
2239 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.
2240 {
2241 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2242 float items_height = window->DC.CursorPos.y - StartPosY;
2243 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2244 Begin(ItemsCount - 1, items_height);
2245 DisplayStart++;
2246 DisplayEnd++;
2247 StepNo = 3;
2248 return true;
2249 }
2250 if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
2251 {
2252 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2253 StepNo = 3;
2254 return true;
2255 }
2256 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.
2257 End();
2258 return false;
2259 }
2260
2261 //-----------------------------------------------------------------------------
2262 // [SECTION] RENDER HELPERS
2263 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2264 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2265 //-----------------------------------------------------------------------------
2266
GetColorU32(ImGuiCol idx,float alpha_mul)2267 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
2268 {
2269 ImGuiStyle& style = GImGui->Style;
2270 ImVec4 c = style.Colors[idx];
2271 c.w *= style.Alpha * alpha_mul;
2272 return ColorConvertFloat4ToU32(c);
2273 }
2274
GetColorU32(const ImVec4 & col)2275 ImU32 ImGui::GetColorU32(const ImVec4& col)
2276 {
2277 ImGuiStyle& style = GImGui->Style;
2278 ImVec4 c = col;
2279 c.w *= style.Alpha;
2280 return ColorConvertFloat4ToU32(c);
2281 }
2282
GetStyleColorVec4(ImGuiCol idx)2283 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
2284 {
2285 ImGuiStyle& style = GImGui->Style;
2286 return style.Colors[idx];
2287 }
2288
GetColorU32(ImU32 col)2289 ImU32 ImGui::GetColorU32(ImU32 col)
2290 {
2291 ImGuiStyle& style = GImGui->Style;
2292 if (style.Alpha >= 1.0f)
2293 return col;
2294 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
2295 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
2296 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
2297 }
2298
FindRenderedTextEnd(const char * text,const char * text_end)2299 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2300 {
2301 const char* text_display_end = text;
2302 if (!text_end)
2303 text_end = (const char*)-1;
2304
2305 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2306 text_display_end++;
2307 return text_display_end;
2308 }
2309
2310 // Internal ImGui functions to render text
2311 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2312 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2313 {
2314 ImGuiContext& g = *GImGui;
2315 ImGuiWindow* window = g.CurrentWindow;
2316
2317 // Hide anything after a '##' string
2318 const char* text_display_end;
2319 if (hide_text_after_hash)
2320 {
2321 text_display_end = FindRenderedTextEnd(text, text_end);
2322 }
2323 else
2324 {
2325 if (!text_end)
2326 text_end = text + strlen(text); // FIXME-OPT
2327 text_display_end = text_end;
2328 }
2329
2330 if (text != text_display_end)
2331 {
2332 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2333 if (g.LogEnabled)
2334 LogRenderedText(&pos, text, text_display_end);
2335 }
2336 }
2337
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2338 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2339 {
2340 ImGuiContext& g = *GImGui;
2341 ImGuiWindow* window = g.CurrentWindow;
2342
2343 if (!text_end)
2344 text_end = text + strlen(text); // FIXME-OPT
2345
2346 if (text != text_end)
2347 {
2348 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2349 if (g.LogEnabled)
2350 LogRenderedText(&pos, text, text_end);
2351 }
2352 }
2353
2354 // Default clip_rect uses (pos_min,pos_max)
2355 // 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)2356 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)
2357 {
2358 // Perform CPU side clipping for single clipped element to avoid using scissor state
2359 ImVec2 pos = pos_min;
2360 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2361
2362 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2363 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2364 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2365 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2366 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2367
2368 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2369 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2370 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2371
2372 // Render
2373 if (need_clipping)
2374 {
2375 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2376 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2377 }
2378 else
2379 {
2380 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2381 }
2382 }
2383
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)2384 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)
2385 {
2386 // Hide anything after a '##' string
2387 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2388 const int text_len = (int)(text_display_end - text);
2389 if (text_len == 0)
2390 return;
2391
2392 ImGuiContext& g = *GImGui;
2393 ImGuiWindow* window = g.CurrentWindow;
2394 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2395 if (g.LogEnabled)
2396 LogRenderedText(&pos_min, text, text_display_end);
2397 }
2398
2399
2400 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2401 // 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.
2402 // 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)2403 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)
2404 {
2405 ImGuiContext& g = *GImGui;
2406 if (text_end_full == NULL)
2407 text_end_full = FindRenderedTextEnd(text);
2408 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2409
2410 //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));
2411 //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));
2412 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2413 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2414 if (text_size.x > pos_max.x - pos_min.x)
2415 {
2416 // Hello wo...
2417 // | | |
2418 // min max ellipsis_max
2419 // <-> this is generally some padding value
2420
2421 const ImFont* font = draw_list->_Data->Font;
2422 const float font_size = draw_list->_Data->FontSize;
2423 const char* text_end_ellipsis = NULL;
2424
2425 ImWchar ellipsis_char = font->EllipsisChar;
2426 int ellipsis_char_count = 1;
2427 if (ellipsis_char == (ImWchar)-1)
2428 {
2429 ellipsis_char = (ImWchar)'.';
2430 ellipsis_char_count = 3;
2431 }
2432 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2433
2434 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2435 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2436
2437 if (ellipsis_char_count > 1)
2438 {
2439 // Full ellipsis size without free spacing after it.
2440 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2441 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2442 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2443 }
2444
2445 // We can now claim the space between pos_max.x and ellipsis_max.x
2446 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2447 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2448 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2449 {
2450 // Always display at least 1 character if there's no room for character + ellipsis
2451 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2452 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2453 }
2454 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2455 {
2456 // 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)
2457 text_end_ellipsis--;
2458 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
2459 }
2460
2461 // Render text, render ellipsis
2462 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2463 float ellipsis_x = pos_min.x + text_size_clipped_x;
2464 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2465 for (int i = 0; i < ellipsis_char_count; i++)
2466 {
2467 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2468 ellipsis_x += ellipsis_glyph_width;
2469 }
2470 }
2471 else
2472 {
2473 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2474 }
2475
2476 if (g.LogEnabled)
2477 LogRenderedText(&pos_min, text, text_end_full);
2478 }
2479
2480 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2481 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2482 {
2483 ImGuiContext& g = *GImGui;
2484 ImGuiWindow* window = g.CurrentWindow;
2485 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2486 const float border_size = g.Style.FrameBorderSize;
2487 if (border && border_size > 0.0f)
2488 {
2489 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2490 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2491 }
2492 }
2493
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2494 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2495 {
2496 ImGuiContext& g = *GImGui;
2497 ImGuiWindow* window = g.CurrentWindow;
2498 const float border_size = g.Style.FrameBorderSize;
2499 if (border_size > 0.0f)
2500 {
2501 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2502 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2503 }
2504 }
2505
2506 // Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
RenderArrow(ImDrawList * draw_list,ImVec2 pos,ImU32 col,ImGuiDir dir,float scale)2507 void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
2508 {
2509 const float h = draw_list->_Data->FontSize * 1.00f;
2510 float r = h * 0.40f * scale;
2511 ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
2512
2513 ImVec2 a, b, c;
2514 switch (dir)
2515 {
2516 case ImGuiDir_Up:
2517 case ImGuiDir_Down:
2518 if (dir == ImGuiDir_Up) r = -r;
2519 a = ImVec2(+0.000f,+0.750f) * r;
2520 b = ImVec2(-0.866f,-0.750f) * r;
2521 c = ImVec2(+0.866f,-0.750f) * r;
2522 break;
2523 case ImGuiDir_Left:
2524 case ImGuiDir_Right:
2525 if (dir == ImGuiDir_Left) r = -r;
2526 a = ImVec2(+0.750f,+0.000f) * r;
2527 b = ImVec2(-0.750f,+0.866f) * r;
2528 c = ImVec2(-0.750f,-0.866f) * r;
2529 break;
2530 case ImGuiDir_None:
2531 case ImGuiDir_COUNT:
2532 IM_ASSERT(0);
2533 break;
2534 }
2535 draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
2536 }
2537
RenderBullet(ImDrawList * draw_list,ImVec2 pos,ImU32 col)2538 void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
2539 {
2540 draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
2541 }
2542
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2543 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2544 {
2545 ImGuiContext& g = *GImGui;
2546 ImGuiWindow* window = g.CurrentWindow;
2547
2548 float thickness = ImMax(sz / 5.0f, 1.0f);
2549 sz -= thickness*0.5f;
2550 pos += ImVec2(thickness*0.25f, thickness*0.25f);
2551
2552 float third = sz / 3.0f;
2553 float bx = pos.x + third;
2554 float by = pos.y + sz - third*0.5f;
2555 window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2556 window->DrawList->PathLineTo(ImVec2(bx, by));
2557 window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2558 window->DrawList->PathStroke(col, false, thickness);
2559 }
2560
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2561 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2562 {
2563 ImGuiContext& g = *GImGui;
2564 if (id != g.NavId)
2565 return;
2566 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2567 return;
2568 ImGuiWindow* window = g.CurrentWindow;
2569 if (window->DC.NavHideHighlightOneFrame)
2570 return;
2571
2572 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2573 ImRect display_rect = bb;
2574 display_rect.ClipWith(window->ClipRect);
2575 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2576 {
2577 const float THICKNESS = 2.0f;
2578 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2579 display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2580 bool fully_visible = window->ClipRect.Contains(display_rect);
2581 if (!fully_visible)
2582 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2583 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);
2584 if (!fully_visible)
2585 window->DrawList->PopClipRect();
2586 }
2587 if (flags & ImGuiNavHighlightFlags_TypeThin)
2588 {
2589 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2590 }
2591 }
2592
2593 //-----------------------------------------------------------------------------
2594 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2595 //-----------------------------------------------------------------------------
2596
2597 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2598 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2599 : DrawListInst(&context->DrawListSharedData)
2600 {
2601 Name = ImStrdup(name);
2602 ID = ImHashStr(name);
2603 IDStack.push_back(ID);
2604 Flags = ImGuiWindowFlags_None;
2605 Pos = ImVec2(0.0f, 0.0f);
2606 Size = SizeFull = ImVec2(0.0f, 0.0f);
2607 ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2608 WindowPadding = ImVec2(0.0f, 0.0f);
2609 WindowRounding = 0.0f;
2610 WindowBorderSize = 0.0f;
2611 NameBufLen = (int)strlen(name) + 1;
2612 MoveId = GetID("#MOVE");
2613 ChildId = 0;
2614 Scroll = ImVec2(0.0f, 0.0f);
2615 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2616 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2617 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2618 ScrollbarX = ScrollbarY = false;
2619 Active = WasActive = false;
2620 WriteAccessed = false;
2621 Collapsed = false;
2622 WantCollapseToggle = false;
2623 SkipItems = false;
2624 Appearing = false;
2625 Hidden = false;
2626 IsFallbackWindow = false;
2627 HasCloseButton = false;
2628 ResizeBorderHeld = -1;
2629 BeginCount = 0;
2630 BeginOrderWithinParent = -1;
2631 BeginOrderWithinContext = -1;
2632 PopupId = 0;
2633 AutoFitFramesX = AutoFitFramesY = -1;
2634 AutoFitChildAxises = 0x00;
2635 AutoFitOnlyGrows = false;
2636 AutoPosLastDirection = ImGuiDir_None;
2637 HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2638 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2639 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2640
2641 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.
2642
2643 LastFrameActive = -1;
2644 LastTimeActive = -1.0f;
2645 ItemWidthDefault = 0.0f;
2646 FontWindowScale = 1.0f;
2647 SettingsOffset = -1;
2648
2649 DrawList = &DrawListInst;
2650 DrawList->_OwnerName = Name;
2651 ParentWindow = NULL;
2652 RootWindow = NULL;
2653 RootWindowForTitleBarHighlight = NULL;
2654 RootWindowForNav = NULL;
2655
2656 NavLastIds[0] = NavLastIds[1] = 0;
2657 NavRectRel[0] = NavRectRel[1] = ImRect();
2658 NavLastChildNavWindow = NULL;
2659
2660 MemoryCompacted = false;
2661 MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2662 }
2663
~ImGuiWindow()2664 ImGuiWindow::~ImGuiWindow()
2665 {
2666 IM_ASSERT(DrawList == &DrawListInst);
2667 IM_DELETE(Name);
2668 for (int i = 0; i != ColumnsStorage.Size; i++)
2669 ColumnsStorage[i].~ImGuiColumns();
2670 }
2671
GetID(const char * str,const char * str_end)2672 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2673 {
2674 ImGuiID seed = IDStack.back();
2675 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2676 ImGui::KeepAliveID(id);
2677 return id;
2678 }
2679
GetID(const void * ptr)2680 ImGuiID ImGuiWindow::GetID(const void* ptr)
2681 {
2682 ImGuiID seed = IDStack.back();
2683 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2684 ImGui::KeepAliveID(id);
2685 return id;
2686 }
2687
GetID(int n)2688 ImGuiID ImGuiWindow::GetID(int n)
2689 {
2690 ImGuiID seed = IDStack.back();
2691 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2692 ImGui::KeepAliveID(id);
2693 return id;
2694 }
2695
GetIDNoKeepAlive(const char * str,const char * str_end)2696 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2697 {
2698 ImGuiID seed = IDStack.back();
2699 return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2700 }
2701
GetIDNoKeepAlive(const void * ptr)2702 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2703 {
2704 ImGuiID seed = IDStack.back();
2705 return ImHashData(&ptr, sizeof(void*), seed);
2706 }
2707
GetIDNoKeepAlive(int n)2708 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2709 {
2710 ImGuiID seed = IDStack.back();
2711 return ImHashData(&n, sizeof(n), seed);
2712 }
2713
2714 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2715 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2716 {
2717 ImGuiID seed = IDStack.back();
2718 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) };
2719 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2720 ImGui::KeepAliveID(id);
2721 return id;
2722 }
2723
SetCurrentWindow(ImGuiWindow * window)2724 static void SetCurrentWindow(ImGuiWindow* window)
2725 {
2726 ImGuiContext& g = *GImGui;
2727 g.CurrentWindow = window;
2728 if (window)
2729 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2730 }
2731
2732 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2733 // This is currently unused by the library, but you may call this yourself for easy GC.
2734 // Not freed:
2735 // - ImGuiWindow, ImGuiWindowSettings, Name
2736 // - StateStorage, ColumnsStorage (may hold useful data)
2737 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2738 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2739 {
2740 window->MemoryCompacted = true;
2741 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2742 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2743 window->IDStack.clear();
2744 window->DrawList->ClearFreeMemory();
2745 window->DC.ChildWindows.clear();
2746 window->DC.ItemFlagsStack.clear();
2747 window->DC.ItemWidthStack.clear();
2748 window->DC.TextWrapPosStack.clear();
2749 window->DC.GroupStack.clear();
2750 }
2751
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2752 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2753 {
2754 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2755 // The other buffers tends to amortize much faster.
2756 window->MemoryCompacted = false;
2757 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2758 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2759 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2760 }
2761
SetActiveID(ImGuiID id,ImGuiWindow * window)2762 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2763 {
2764 ImGuiContext& g = *GImGui;
2765 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2766 if (g.ActiveIdIsJustActivated)
2767 {
2768 g.ActiveIdTimer = 0.0f;
2769 g.ActiveIdHasBeenPressedBefore = false;
2770 g.ActiveIdHasBeenEditedBefore = false;
2771 if (id != 0)
2772 {
2773 g.LastActiveId = id;
2774 g.LastActiveIdTimer = 0.0f;
2775 }
2776 }
2777 g.ActiveId = id;
2778 g.ActiveIdAllowOverlap = false;
2779 g.ActiveIdWindow = window;
2780 g.ActiveIdHasBeenEditedThisFrame = false;
2781 if (id)
2782 {
2783 g.ActiveIdIsAlive = id;
2784 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2785 }
2786
2787 // Clear declaration of inputs claimed by the widget
2788 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2789 g.ActiveIdUsingNavDirMask = 0x00;
2790 g.ActiveIdUsingNavInputMask = 0x00;
2791 g.ActiveIdUsingKeyInputMask = 0x00;
2792 }
2793
ClearActiveID()2794 void ImGui::ClearActiveID()
2795 {
2796 SetActiveID(0, NULL);
2797 }
2798
SetHoveredID(ImGuiID id)2799 void ImGui::SetHoveredID(ImGuiID id)
2800 {
2801 ImGuiContext& g = *GImGui;
2802 g.HoveredId = id;
2803 g.HoveredIdAllowOverlap = false;
2804 if (id != 0 && g.HoveredIdPreviousFrame != id)
2805 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2806 }
2807
GetHoveredID()2808 ImGuiID ImGui::GetHoveredID()
2809 {
2810 ImGuiContext& g = *GImGui;
2811 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2812 }
2813
KeepAliveID(ImGuiID id)2814 void ImGui::KeepAliveID(ImGuiID id)
2815 {
2816 ImGuiContext& g = *GImGui;
2817 if (g.ActiveId == id)
2818 g.ActiveIdIsAlive = id;
2819 if (g.ActiveIdPreviousFrame == id)
2820 g.ActiveIdPreviousFrameIsAlive = true;
2821 }
2822
MarkItemEdited(ImGuiID id)2823 void ImGui::MarkItemEdited(ImGuiID id)
2824 {
2825 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2826 // 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.
2827 ImGuiContext& g = *GImGui;
2828 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2829 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2830 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2831 g.ActiveIdHasBeenEditedThisFrame = true;
2832 g.ActiveIdHasBeenEditedBefore = true;
2833 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2834 }
2835
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2836 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2837 {
2838 // An active popup disable hovering on other windows (apart from its own children)
2839 // FIXME-OPT: This could be cached/stored within the window.
2840 ImGuiContext& g = *GImGui;
2841 if (g.NavWindow)
2842 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2843 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2844 {
2845 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2846 // NB: The order of those two tests is important because Modal windows are also Popups.
2847 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2848 return false;
2849 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2850 return false;
2851 }
2852 return true;
2853 }
2854
2855 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_baseline_y)2856 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
2857 {
2858 ImGuiContext& g = *GImGui;
2859 ImGuiWindow* window = g.CurrentWindow;
2860 if (window->SkipItems)
2861 return;
2862
2863 // We increase the height in this function to accommodate for baseline offset.
2864 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
2865 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
2866 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
2867 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
2868
2869 // Always align ourselves on pixel boundaries
2870 //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]
2871 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
2872 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
2873 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
2874 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
2875 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2876 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2877 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2878
2879 window->DC.PrevLineSize.y = line_height;
2880 window->DC.CurrLineSize.y = 0.0f;
2881 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
2882 window->DC.CurrLineTextBaseOffset = 0.0f;
2883
2884 // Horizontal layout mode
2885 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2886 SameLine();
2887 }
2888
ItemSize(const ImRect & bb,float text_baseline_y)2889 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
2890 {
2891 ItemSize(bb.GetSize(), text_baseline_y);
2892 }
2893
2894 // Declare item bounding box for clipping and interaction.
2895 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2896 // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd().
ItemAdd(const ImRect & bb,ImGuiID id,const ImRect * nav_bb_arg)2897 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2898 {
2899 ImGuiContext& g = *GImGui;
2900 ImGuiWindow* window = g.CurrentWindow;
2901
2902 if (id != 0)
2903 {
2904 // Navigation processing runs prior to clipping early-out
2905 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2906 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
2907 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
2908 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2909 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
2910 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
2911 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
2912 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
2913 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2914 if (g.NavId == id || g.NavAnyRequest)
2915 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2916 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2917 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2918
2919 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
2920 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
2921 if (id == g.DebugItemPickerBreakId)
2922 {
2923 IM_DEBUG_BREAK();
2924 g.DebugItemPickerBreakId = 0;
2925 }
2926 #endif
2927 }
2928
2929 window->DC.LastItemId = id;
2930 window->DC.LastItemRect = bb;
2931 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2932 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
2933
2934 #ifdef IMGUI_ENABLE_TEST_ENGINE
2935 if (id != 0)
2936 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
2937 #endif
2938
2939 // Clipping test
2940 const bool is_clipped = IsClippedEx(bb, id, false);
2941 if (is_clipped)
2942 return false;
2943 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2944
2945 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2946 if (IsMouseHoveringRect(bb.Min, bb.Max))
2947 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2948 return true;
2949 }
2950
2951 // This is roughly matching the behavior of internal-facing ItemHoverable()
2952 // - 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()
2953 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2954 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2955 {
2956 ImGuiContext& g = *GImGui;
2957 ImGuiWindow* window = g.CurrentWindow;
2958 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2959 return IsItemFocused();
2960
2961 // Test for bounding box overlap, as updated as ItemAdd()
2962 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2963 return false;
2964 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2965
2966 // Test if we are hovering the right window (our window could be behind another window)
2967 // [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.
2968 // 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.
2969 //if (g.HoveredWindow != window)
2970 // return false;
2971 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2972 return false;
2973
2974 // Test if another item is active (e.g. being dragged)
2975 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2976 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2977 return false;
2978
2979 // Test if interactions on this window are blocked by an active popup or modal.
2980 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
2981 if (!IsWindowContentHoverable(window, flags))
2982 return false;
2983
2984 // Test if the item is disabled
2985 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2986 return false;
2987
2988 // Special handling for the dummy item after Begin() which represent the title bar or tab.
2989 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2990 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2991 return false;
2992 return true;
2993 }
2994
2995 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2996 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2997 {
2998 ImGuiContext& g = *GImGui;
2999 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
3000 return false;
3001
3002 ImGuiWindow* window = g.CurrentWindow;
3003 if (g.HoveredWindow != window)
3004 return false;
3005 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
3006 return false;
3007 if (!IsMouseHoveringRect(bb.Min, bb.Max))
3008 return false;
3009 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
3010 return false;
3011 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
3012 return false;
3013
3014 SetHoveredID(id);
3015
3016 // [DEBUG] Item Picker tool!
3017 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
3018 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
3019 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
3020 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
3021 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
3022 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
3023 if (g.DebugItemPickerBreakId == id)
3024 IM_DEBUG_BREAK();
3025
3026 return true;
3027 }
3028
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)3029 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
3030 {
3031 ImGuiContext& g = *GImGui;
3032 ImGuiWindow* window = g.CurrentWindow;
3033 if (!bb.Overlaps(window->ClipRect))
3034 if (id == 0 || id != g.ActiveId)
3035 if (clip_even_when_logged || !g.LogEnabled)
3036 return true;
3037 return false;
3038 }
3039
3040 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)3041 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
3042 {
3043 ImGuiContext& g = *GImGui;
3044
3045 // Increment counters
3046 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
3047 window->DC.FocusCounterRegular++;
3048 if (is_tab_stop)
3049 window->DC.FocusCounterTabStop++;
3050
3051 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
3052 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
3053 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
3054 {
3055 g.FocusRequestNextWindow = window;
3056 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.
3057 }
3058
3059 // Handle focus requests
3060 if (g.FocusRequestCurrWindow == window)
3061 {
3062 if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular)
3063 return true;
3064 if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop)
3065 {
3066 g.NavJustTabbedId = id;
3067 return true;
3068 }
3069
3070 // If another item is about to be focused, we clear our own active id
3071 if (g.ActiveId == id)
3072 ClearActiveID();
3073 }
3074
3075 return false;
3076 }
3077
FocusableItemUnregister(ImGuiWindow * window)3078 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3079 {
3080 window->DC.FocusCounterRegular--;
3081 window->DC.FocusCounterTabStop--;
3082 }
3083
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3084 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3085 {
3086 if (wrap_pos_x < 0.0f)
3087 return 0.0f;
3088
3089 ImGuiWindow* window = GImGui->CurrentWindow;
3090 if (wrap_pos_x == 0.0f)
3091 wrap_pos_x = window->WorkRect.Max.x;
3092 else if (wrap_pos_x > 0.0f)
3093 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3094
3095 return ImMax(wrap_pos_x - pos.x, 1.0f);
3096 }
3097
3098 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3099 void* ImGui::MemAlloc(size_t size)
3100 {
3101 if (ImGuiContext* ctx = GImGui)
3102 ctx->IO.MetricsActiveAllocations++;
3103 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3104 }
3105
3106 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3107 void ImGui::MemFree(void* ptr)
3108 {
3109 if (ptr)
3110 if (ImGuiContext* ctx = GImGui)
3111 ctx->IO.MetricsActiveAllocations--;
3112 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3113 }
3114
GetClipboardText()3115 const char* ImGui::GetClipboardText()
3116 {
3117 ImGuiContext& g = *GImGui;
3118 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3119 }
3120
SetClipboardText(const char * text)3121 void ImGui::SetClipboardText(const char* text)
3122 {
3123 ImGuiContext& g = *GImGui;
3124 if (g.IO.SetClipboardTextFn)
3125 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3126 }
3127
GetVersion()3128 const char* ImGui::GetVersion()
3129 {
3130 return IMGUI_VERSION;
3131 }
3132
3133 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3134 // 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()3135 ImGuiContext* ImGui::GetCurrentContext()
3136 {
3137 return GImGui;
3138 }
3139
SetCurrentContext(ImGuiContext * ctx)3140 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3141 {
3142 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3143 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3144 #else
3145 GImGui = ctx;
3146 #endif
3147 }
3148
3149 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
3150 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3151 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
3152 // may see different structures than what imgui.cpp sees, which is problematic.
3153 // 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)3154 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)
3155 {
3156 bool error = false;
3157 if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
3158 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
3159 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
3160 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
3161 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
3162 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
3163 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
3164 return !error;
3165 }
3166
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3167 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3168 {
3169 GImAllocatorAllocFunc = alloc_func;
3170 GImAllocatorFreeFunc = free_func;
3171 GImAllocatorUserData = user_data;
3172 }
3173
CreateContext(ImFontAtlas * shared_font_atlas)3174 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3175 {
3176 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3177 if (GImGui == NULL)
3178 SetCurrentContext(ctx);
3179 Initialize(ctx);
3180 return ctx;
3181 }
3182
DestroyContext(ImGuiContext * ctx)3183 void ImGui::DestroyContext(ImGuiContext* ctx)
3184 {
3185 if (ctx == NULL)
3186 ctx = GImGui;
3187 Shutdown(ctx);
3188 if (GImGui == ctx)
3189 SetCurrentContext(NULL);
3190 IM_DELETE(ctx);
3191 }
3192
GetIO()3193 ImGuiIO& ImGui::GetIO()
3194 {
3195 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3196 return GImGui->IO;
3197 }
3198
GetStyle()3199 ImGuiStyle& ImGui::GetStyle()
3200 {
3201 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3202 return GImGui->Style;
3203 }
3204
3205 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3206 ImDrawData* ImGui::GetDrawData()
3207 {
3208 ImGuiContext& g = *GImGui;
3209 return g.DrawData.Valid ? &g.DrawData : NULL;
3210 }
3211
GetTime()3212 double ImGui::GetTime()
3213 {
3214 return GImGui->Time;
3215 }
3216
GetFrameCount()3217 int ImGui::GetFrameCount()
3218 {
3219 return GImGui->FrameCount;
3220 }
3221
GetBackgroundDrawList()3222 ImDrawList* ImGui::GetBackgroundDrawList()
3223 {
3224 return &GImGui->BackgroundDrawList;
3225 }
3226
GetForegroundDrawList()3227 ImDrawList* ImGui::GetForegroundDrawList()
3228 {
3229 return &GImGui->ForegroundDrawList;
3230 }
3231
GetDrawListSharedData()3232 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3233 {
3234 return &GImGui->DrawListSharedData;
3235 }
3236
StartMouseMovingWindow(ImGuiWindow * window)3237 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3238 {
3239 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3240 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3241 // This is because we want ActiveId to be set even when the window is not permitted to move.
3242 ImGuiContext& g = *GImGui;
3243 FocusWindow(window);
3244 SetActiveID(window->MoveId, window);
3245 g.NavDisableHighlight = true;
3246 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3247
3248 bool can_move_window = true;
3249 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3250 can_move_window = false;
3251 if (can_move_window)
3252 g.MovingWindow = window;
3253 }
3254
3255 // Handle mouse moving window
3256 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3257 void ImGui::UpdateMouseMovingWindowNewFrame()
3258 {
3259 ImGuiContext& g = *GImGui;
3260 if (g.MovingWindow != NULL)
3261 {
3262 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3263 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3264 KeepAliveID(g.ActiveId);
3265 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3266 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3267 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3268 {
3269 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3270 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3271 {
3272 MarkIniSettingsDirty(moving_window);
3273 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3274 }
3275 FocusWindow(g.MovingWindow);
3276 }
3277 else
3278 {
3279 ClearActiveID();
3280 g.MovingWindow = NULL;
3281 }
3282 }
3283 else
3284 {
3285 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3286 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3287 {
3288 KeepAliveID(g.ActiveId);
3289 if (!g.IO.MouseDown[0])
3290 ClearActiveID();
3291 }
3292 }
3293 }
3294
3295 // Initiate moving window when clicking on empty space or title bar.
3296 // Handle left-click and right-click focus.
UpdateMouseMovingWindowEndFrame()3297 void ImGui::UpdateMouseMovingWindowEndFrame()
3298 {
3299 ImGuiContext& g = *GImGui;
3300 if (g.ActiveId != 0 || g.HoveredId != 0)
3301 return;
3302
3303 // Unless we just made a window/popup appear
3304 if (g.NavWindow && g.NavWindow->Appearing)
3305 return;
3306
3307 // Click to focus window and start moving (after we're done with all our widgets)
3308 if (g.IO.MouseClicked[0])
3309 {
3310 if (g.HoveredRootWindow != NULL)
3311 {
3312 StartMouseMovingWindow(g.HoveredWindow);
3313 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3314 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3315 g.MovingWindow = NULL;
3316 }
3317 else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3318 {
3319 // Clicking on void disable focus
3320 FocusWindow(NULL);
3321 }
3322 }
3323
3324 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3325 // Instead, focus will be restored to the window under the bottom-most closed popup.
3326 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3327 if (g.IO.MouseClicked[1])
3328 {
3329 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3330 // This is where we can trim the popup stack.
3331 ImGuiWindow* modal = GetTopMostPopupModal();
3332 bool hovered_window_above_modal = false;
3333 if (modal == NULL)
3334 hovered_window_above_modal = true;
3335 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3336 {
3337 ImGuiWindow* window = g.Windows[i];
3338 if (window == modal)
3339 break;
3340 if (window == g.HoveredWindow)
3341 hovered_window_above_modal = true;
3342 }
3343 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3344 }
3345 }
3346
IsWindowActiveAndVisible(ImGuiWindow * window)3347 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3348 {
3349 return (window->Active) && (!window->Hidden);
3350 }
3351
UpdateMouseInputs()3352 static void ImGui::UpdateMouseInputs()
3353 {
3354 ImGuiContext& g = *GImGui;
3355
3356 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3357 if (IsMousePosValid(&g.IO.MousePos))
3358 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3359
3360 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3361 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3362 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3363 else
3364 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3365 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3366 g.NavDisableMouseHover = false;
3367
3368 g.IO.MousePosPrev = g.IO.MousePos;
3369 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3370 {
3371 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3372 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3373 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3374 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;
3375 g.IO.MouseDoubleClicked[i] = false;
3376 if (g.IO.MouseClicked[i])
3377 {
3378 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3379 {
3380 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3381 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3382 g.IO.MouseDoubleClicked[i] = true;
3383 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
3384 }
3385 else
3386 {
3387 g.IO.MouseClickedTime[i] = g.Time;
3388 }
3389 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3390 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3391 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3392 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3393 }
3394 else if (g.IO.MouseDown[i])
3395 {
3396 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3397 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3398 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3399 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);
3400 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);
3401 }
3402 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3403 g.IO.MouseDownWasDoubleClick[i] = false;
3404 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3405 g.NavDisableMouseHover = false;
3406 }
3407 }
3408
StartLockWheelingWindow(ImGuiWindow * window)3409 static void StartLockWheelingWindow(ImGuiWindow* window)
3410 {
3411 ImGuiContext& g = *GImGui;
3412 if (g.WheelingWindow == window)
3413 return;
3414 g.WheelingWindow = window;
3415 g.WheelingWindowRefMousePos = g.IO.MousePos;
3416 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3417 }
3418
UpdateMouseWheel()3419 void ImGui::UpdateMouseWheel()
3420 {
3421 ImGuiContext& g = *GImGui;
3422
3423 // Reset the locked window if we move the mouse or after the timer elapses
3424 if (g.WheelingWindow != NULL)
3425 {
3426 g.WheelingWindowTimer -= g.IO.DeltaTime;
3427 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3428 g.WheelingWindowTimer = 0.0f;
3429 if (g.WheelingWindowTimer <= 0.0f)
3430 {
3431 g.WheelingWindow = NULL;
3432 g.WheelingWindowTimer = 0.0f;
3433 }
3434 }
3435
3436 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3437 return;
3438
3439 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3440 if (!window || window->Collapsed)
3441 return;
3442
3443 // Zoom / Scale window
3444 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3445 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3446 {
3447 StartLockWheelingWindow(window);
3448 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3449 const float scale = new_font_scale / window->FontWindowScale;
3450 window->FontWindowScale = new_font_scale;
3451 if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3452 {
3453 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3454 SetWindowPos(window, window->Pos + offset, 0);
3455 window->Size = ImFloor(window->Size * scale);
3456 window->SizeFull = ImFloor(window->SizeFull * scale);
3457 }
3458 return;
3459 }
3460
3461 // Mouse wheel scrolling
3462 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3463
3464 // Vertical Mouse Wheel scrolling
3465 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3466 if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3467 {
3468 StartLockWheelingWindow(window);
3469 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3470 window = window->ParentWindow;
3471 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3472 {
3473 float max_step = window->InnerRect.GetHeight() * 0.67f;
3474 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3475 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3476 }
3477 }
3478
3479 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3480 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;
3481 if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3482 {
3483 StartLockWheelingWindow(window);
3484 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3485 window = window->ParentWindow;
3486 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3487 {
3488 float max_step = window->InnerRect.GetWidth() * 0.67f;
3489 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3490 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3491 }
3492 }
3493 }
3494
3495 // 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()3496 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3497 {
3498 ImGuiContext& g = *GImGui;
3499
3500 // Find the window hovered by mouse:
3501 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3502 // - 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.
3503 // - 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.
3504 FindHoveredWindow();
3505
3506 // Modal windows prevents cursor from hovering behind them.
3507 ImGuiWindow* modal_window = GetTopMostPopupModal();
3508 if (modal_window)
3509 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3510 g.HoveredRootWindow = g.HoveredWindow = NULL;
3511
3512 // Disabled mouse?
3513 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3514 g.HoveredWindow = g.HoveredRootWindow = NULL;
3515
3516 // 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.
3517 int mouse_earliest_button_down = -1;
3518 bool mouse_any_down = false;
3519 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3520 {
3521 if (g.IO.MouseClicked[i])
3522 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3523 mouse_any_down |= g.IO.MouseDown[i];
3524 if (g.IO.MouseDown[i])
3525 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3526 mouse_earliest_button_down = i;
3527 }
3528 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3529
3530 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3531 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3532 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3533 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3534 g.HoveredWindow = g.HoveredRootWindow = NULL;
3535
3536 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3537 if (g.WantCaptureMouseNextFrame != -1)
3538 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3539 else
3540 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3541
3542 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3543 if (g.WantCaptureKeyboardNextFrame != -1)
3544 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3545 else
3546 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3547 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3548 g.IO.WantCaptureKeyboard = true;
3549
3550 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3551 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3552 }
3553
NewFrameSanityChecks()3554 static void NewFrameSanityChecks()
3555 {
3556 ImGuiContext& g = *GImGui;
3557
3558 // Check user data
3559 // (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)
3560 IM_ASSERT(g.Initialized);
3561 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
3562 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3563 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
3564 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3565 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3566 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
3567 IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!");
3568 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)!");
3569 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
3570 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
3571 for (int n = 0; n < ImGuiKey_COUNT; n++)
3572 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)");
3573
3574 // 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)
3575 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3576 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3577
3578 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3579 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3580 g.IO.ConfigWindowsResizeFromEdges = false;
3581 }
3582
NewFrame()3583 void ImGui::NewFrame()
3584 {
3585 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3586 ImGuiContext& g = *GImGui;
3587
3588 #ifdef IMGUI_ENABLE_TEST_ENGINE
3589 ImGuiTestEngineHook_PreNewFrame(&g);
3590 #endif
3591
3592 // Check and assert for various common IO and Configuration mistakes
3593 NewFrameSanityChecks();
3594
3595 // Load settings on first frame (if not explicitly loaded manually before)
3596 if (!g.SettingsLoaded)
3597 {
3598 IM_ASSERT(g.SettingsWindows.empty());
3599 if (g.IO.IniFilename)
3600 LoadIniSettingsFromDisk(g.IO.IniFilename);
3601 g.SettingsLoaded = true;
3602 }
3603
3604 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3605 if (g.SettingsDirtyTimer > 0.0f)
3606 {
3607 g.SettingsDirtyTimer -= g.IO.DeltaTime;
3608 if (g.SettingsDirtyTimer <= 0.0f)
3609 {
3610 if (g.IO.IniFilename != NULL)
3611 SaveIniSettingsToDisk(g.IO.IniFilename);
3612 else
3613 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3614 g.SettingsDirtyTimer = 0.0f;
3615 }
3616 }
3617
3618 g.Time += g.IO.DeltaTime;
3619 g.WithinFrameScope = true;
3620 g.FrameCount += 1;
3621 g.TooltipOverrideCount = 0;
3622 g.WindowsActiveCount = 0;
3623
3624 // Setup current font and draw list shared data
3625 g.IO.Fonts->Locked = true;
3626 SetCurrentFont(GetDefaultFont());
3627 IM_ASSERT(g.Font->IsLoaded());
3628 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3629 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3630 g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
3631 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3632 if (g.Style.AntiAliasedLines)
3633 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3634 if (g.Style.AntiAliasedFill)
3635 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3636 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3637 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3638
3639 g.BackgroundDrawList.Clear();
3640 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3641 g.BackgroundDrawList.PushClipRectFullScreen();
3642
3643 g.ForegroundDrawList.Clear();
3644 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3645 g.ForegroundDrawList.PushClipRectFullScreen();
3646
3647 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3648 g.DrawData.Clear();
3649
3650 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3651 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3652 KeepAliveID(g.DragDropPayload.SourceId);
3653
3654 // Clear reference to active widget if the widget isn't alive anymore
3655 if (!g.HoveredIdPreviousFrame)
3656 g.HoveredIdTimer = 0.0f;
3657 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3658 g.HoveredIdNotActiveTimer = 0.0f;
3659 if (g.HoveredId)
3660 g.HoveredIdTimer += g.IO.DeltaTime;
3661 if (g.HoveredId && g.ActiveId != g.HoveredId)
3662 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3663 g.HoveredIdPreviousFrame = g.HoveredId;
3664 g.HoveredId = 0;
3665 g.HoveredIdAllowOverlap = false;
3666 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3667 ClearActiveID();
3668 if (g.ActiveId)
3669 g.ActiveIdTimer += g.IO.DeltaTime;
3670 g.LastActiveIdTimer += g.IO.DeltaTime;
3671 g.ActiveIdPreviousFrame = g.ActiveId;
3672 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3673 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3674 g.ActiveIdIsAlive = 0;
3675 g.ActiveIdHasBeenEditedThisFrame = false;
3676 g.ActiveIdPreviousFrameIsAlive = false;
3677 g.ActiveIdIsJustActivated = false;
3678 if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
3679 g.TempInputTextId = 0;
3680 if (g.ActiveId == 0)
3681 {
3682 g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3683 g.ActiveIdUsingKeyInputMask = 0;
3684 }
3685
3686 // Drag and drop
3687 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3688 g.DragDropAcceptIdCurr = 0;
3689 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3690 g.DragDropWithinSourceOrTarget = false;
3691
3692 // Update keyboard input state
3693 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3694 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3695 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;
3696
3697 // Update gamepad/keyboard directional navigation
3698 NavUpdate();
3699
3700 // Update mouse input state
3701 UpdateMouseInputs();
3702
3703 // Calculate frame-rate for the user, as a purely luxurious feature
3704 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3705 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3706 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3707 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3708
3709 // Find hovered window
3710 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3711 UpdateHoveredWindowAndCaptureFlags();
3712
3713 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3714 UpdateMouseMovingWindowNewFrame();
3715
3716 // Background darkening/whitening
3717 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3718 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3719 else
3720 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3721
3722 g.MouseCursor = ImGuiMouseCursor_Arrow;
3723 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3724 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3725
3726 // Mouse wheel scrolling, scale
3727 UpdateMouseWheel();
3728
3729 // Pressing TAB activate widget focus
3730 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3731 if (g.ActiveId == 0 && g.FocusTabPressed)
3732 {
3733 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3734 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3735 g.FocusRequestNextWindow = g.NavWindow;
3736 g.FocusRequestNextCounterRegular = INT_MAX;
3737 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3738 g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3739 else
3740 g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0;
3741 }
3742
3743 // Turn queued focus request into current one
3744 g.FocusRequestCurrWindow = NULL;
3745 g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX;
3746 if (g.FocusRequestNextWindow != NULL)
3747 {
3748 ImGuiWindow* window = g.FocusRequestNextWindow;
3749 g.FocusRequestCurrWindow = window;
3750 if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1)
3751 g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1);
3752 if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1)
3753 g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1);
3754 g.FocusRequestNextWindow = NULL;
3755 g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX;
3756 }
3757
3758 g.NavIdTabCounter = INT_MAX;
3759
3760 // Mark all windows as not visible and compact unused memory.
3761 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3762 const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3763 for (int i = 0; i != g.Windows.Size; i++)
3764 {
3765 ImGuiWindow* window = g.Windows[i];
3766 window->WasActive = window->Active;
3767 window->BeginCount = 0;
3768 window->Active = false;
3769 window->WriteAccessed = false;
3770
3771 // Garbage collect transient buffers of recently unused windows
3772 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3773 GcCompactTransientWindowBuffers(window);
3774 }
3775
3776 // Closing the focused window restore focus to the first active root window in descending z-order
3777 if (g.NavWindow && !g.NavWindow->WasActive)
3778 FocusTopMostWindowUnderOne(NULL, NULL);
3779
3780 // No window should be open at the beginning of the frame.
3781 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3782 g.CurrentWindowStack.resize(0);
3783 g.BeginPopupStack.resize(0);
3784 ClosePopupsOverWindow(g.NavWindow, false);
3785
3786 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3787 UpdateDebugToolItemPicker();
3788
3789 // Create implicit/fallback window - which we will only render it if the user has added something to it.
3790 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3791 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3792 g.WithinFrameScopeWithImplicitWindow = true;
3793 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3794 Begin("Debug##Default");
3795 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
3796
3797 #ifdef IMGUI_ENABLE_TEST_ENGINE
3798 ImGuiTestEngineHook_PostNewFrame(&g);
3799 #endif
3800 }
3801
3802 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3803 void ImGui::UpdateDebugToolItemPicker()
3804 {
3805 ImGuiContext& g = *GImGui;
3806 g.DebugItemPickerBreakId = 0;
3807 if (g.DebugItemPickerActive)
3808 {
3809 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3810 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3811 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3812 g.DebugItemPickerActive = false;
3813 if (ImGui::IsMouseClicked(0) && hovered_id)
3814 {
3815 g.DebugItemPickerBreakId = hovered_id;
3816 g.DebugItemPickerActive = false;
3817 }
3818 ImGui::SetNextWindowBgAlpha(0.60f);
3819 ImGui::BeginTooltip();
3820 ImGui::Text("HoveredId: 0x%08X", hovered_id);
3821 ImGui::Text("Press ESC to abort picking.");
3822 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3823 ImGui::EndTooltip();
3824 }
3825 }
3826
Initialize(ImGuiContext * context)3827 void ImGui::Initialize(ImGuiContext* context)
3828 {
3829 ImGuiContext& g = *context;
3830 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3831
3832 // Add .ini handle for ImGuiWindow type
3833 {
3834 ImGuiSettingsHandler ini_handler;
3835 ini_handler.TypeName = "Window";
3836 ini_handler.TypeHash = ImHashStr("Window");
3837 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
3838 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
3839 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
3840 g.SettingsHandlers.push_back(ini_handler);
3841 }
3842
3843 #ifdef IMGUI_HAS_TABLE
3844 // Add .ini handle for ImGuiTable type
3845 {
3846 ImGuiSettingsHandler ini_handler;
3847 ini_handler.TypeName = "Table";
3848 ini_handler.TypeHash = ImHashStr("Table");
3849 ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
3850 ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
3851 ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
3852 g.SettingsHandlers.push_back(ini_handler);
3853 }
3854 #endif // #ifdef IMGUI_HAS_TABLE
3855
3856 #ifdef IMGUI_HAS_DOCK
3857 #endif // #ifdef IMGUI_HAS_DOCK
3858
3859 g.Initialized = true;
3860 }
3861
3862 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3863 void ImGui::Shutdown(ImGuiContext* context)
3864 {
3865 // 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)
3866 ImGuiContext& g = *context;
3867 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3868 {
3869 g.IO.Fonts->Locked = false;
3870 IM_DELETE(g.IO.Fonts);
3871 }
3872 g.IO.Fonts = NULL;
3873
3874 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3875 if (!g.Initialized)
3876 return;
3877
3878 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3879 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3880 {
3881 ImGuiContext* backup_context = GImGui;
3882 SetCurrentContext(context);
3883 SaveIniSettingsToDisk(g.IO.IniFilename);
3884 SetCurrentContext(backup_context);
3885 }
3886
3887 // Clear everything else
3888 for (int i = 0; i < g.Windows.Size; i++)
3889 IM_DELETE(g.Windows[i]);
3890 g.Windows.clear();
3891 g.WindowsFocusOrder.clear();
3892 g.WindowsTempSortBuffer.clear();
3893 g.CurrentWindow = NULL;
3894 g.CurrentWindowStack.clear();
3895 g.WindowsById.Clear();
3896 g.NavWindow = NULL;
3897 g.HoveredWindow = g.HoveredRootWindow = NULL;
3898 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3899 g.MovingWindow = NULL;
3900 g.ColorModifiers.clear();
3901 g.StyleModifiers.clear();
3902 g.FontStack.clear();
3903 g.OpenPopupStack.clear();
3904 g.BeginPopupStack.clear();
3905 g.DrawDataBuilder.ClearFreeMemory();
3906 g.BackgroundDrawList.ClearFreeMemory();
3907 g.ForegroundDrawList.ClearFreeMemory();
3908
3909 g.TabBars.Clear();
3910 g.CurrentTabBarStack.clear();
3911 g.ShrinkWidthBuffer.clear();
3912
3913 g.PrivateClipboard.clear();
3914 g.InputTextState.ClearFreeMemory();
3915
3916 g.SettingsWindows.clear();
3917 g.SettingsHandlers.clear();
3918
3919 if (g.LogFile)
3920 {
3921 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
3922 if (g.LogFile != stdout)
3923 #endif
3924 ImFileClose(g.LogFile);
3925 g.LogFile = NULL;
3926 }
3927 g.LogBuffer.clear();
3928
3929 g.Initialized = false;
3930 }
3931
3932 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3933 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3934 {
3935 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3936 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3937 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3938 return d;
3939 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3940 return d;
3941 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3942 }
3943
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3944 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3945 {
3946 out_sorted_windows->push_back(window);
3947 if (window->Active)
3948 {
3949 int count = window->DC.ChildWindows.Size;
3950 if (count > 1)
3951 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3952 for (int i = 0; i < count; i++)
3953 {
3954 ImGuiWindow* child = window->DC.ChildWindows[i];
3955 if (child->Active)
3956 AddWindowToSortBuffer(out_sorted_windows, child);
3957 }
3958 }
3959 }
3960
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3961 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3962 {
3963 if (draw_list->CmdBuffer.empty())
3964 return;
3965
3966 // Remove trailing command if unused
3967 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3968 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3969 {
3970 draw_list->CmdBuffer.pop_back();
3971 if (draw_list->CmdBuffer.empty())
3972 return;
3973 }
3974
3975 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
3976 // May trigger for you if you are using PrimXXX functions incorrectly.
3977 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3978 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3979 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
3980 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3981
3982 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3983 // If this assert triggers because you are drawing lots of stuff manually:
3984 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
3985 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
3986 // - If you want large meshes with more than 64K vertices, you can either:
3987 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
3988 // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
3989 // 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.
3990 // (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
3991 // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
3992 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3993 // Your own engine or render API may use different parameters or function calls to specify index sizes.
3994 // 2 and 4 bytes indices are generally supported by most graphics API.
3995 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
3996 // the 64K limit to split your draw commands in multiple draw lists.
3997 if (sizeof(ImDrawIdx) == 2)
3998 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3999
4000 out_list->push_back(draw_list);
4001 }
4002
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)4003 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
4004 {
4005 ImGuiContext& g = *GImGui;
4006 g.IO.MetricsRenderWindows++;
4007 AddDrawListToDrawData(out_render_list, window->DrawList);
4008 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
4009 {
4010 ImGuiWindow* child = window->DC.ChildWindows[i];
4011 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
4012 AddWindowToDrawData(out_render_list, child);
4013 }
4014 }
4015
4016 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)4017 static void AddRootWindowToDrawData(ImGuiWindow* window)
4018 {
4019 ImGuiContext& g = *GImGui;
4020 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
4021 AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window);
4022 }
4023
FlattenIntoSingleLayer()4024 void ImDrawDataBuilder::FlattenIntoSingleLayer()
4025 {
4026 int n = Layers[0].Size;
4027 int size = n;
4028 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
4029 size += Layers[i].Size;
4030 Layers[0].resize(size);
4031 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
4032 {
4033 ImVector<ImDrawList*>& layer = Layers[layer_n];
4034 if (layer.empty())
4035 continue;
4036 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
4037 n += layer.Size;
4038 layer.resize(0);
4039 }
4040 }
4041
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)4042 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
4043 {
4044 ImGuiIO& io = ImGui::GetIO();
4045 draw_data->Valid = true;
4046 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
4047 draw_data->CmdListsCount = draw_lists->Size;
4048 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
4049 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
4050 draw_data->DisplaySize = io.DisplaySize;
4051 draw_data->FramebufferScale = io.DisplayFramebufferScale;
4052 for (int n = 0; n < draw_lists->Size; n++)
4053 {
4054 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
4055 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
4056 }
4057 }
4058
4059 // When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result.
PushClipRect(const ImVec2 & clip_rect_min,const ImVec2 & clip_rect_max,bool intersect_with_current_clip_rect)4060 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
4061 {
4062 ImGuiWindow* window = GetCurrentWindow();
4063 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
4064 window->ClipRect = window->DrawList->_ClipRectStack.back();
4065 }
4066
PopClipRect()4067 void ImGui::PopClipRect()
4068 {
4069 ImGuiWindow* window = GetCurrentWindow();
4070 window->DrawList->PopClipRect();
4071 window->ClipRect = window->DrawList->_ClipRectStack.back();
4072 }
4073
4074 // 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()4075 void ImGui::EndFrame()
4076 {
4077 ImGuiContext& g = *GImGui;
4078 IM_ASSERT(g.Initialized);
4079 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
4080 return;
4081 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4082
4083 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4084 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4085 {
4086 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4087 g.PlatformImeLastPos = g.PlatformImePos;
4088 }
4089
4090 ErrorCheckEndFrame();
4091
4092 // Hide implicit/fallback "Debug" window if it hasn't been used
4093 g.WithinFrameScopeWithImplicitWindow = false;
4094 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4095 g.CurrentWindow->Active = false;
4096 End();
4097
4098 // Show CTRL+TAB list window
4099 if (g.NavWindowingTarget != NULL)
4100 NavUpdateWindowingOverlay();
4101
4102 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4103 if (g.DragDropActive)
4104 {
4105 bool is_delivered = g.DragDropPayload.Delivery;
4106 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4107 if (is_delivered || is_elapsed)
4108 ClearDragDrop();
4109 }
4110
4111 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4112 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
4113 {
4114 g.DragDropWithinSourceOrTarget = true;
4115 SetTooltip("...");
4116 g.DragDropWithinSourceOrTarget = false;
4117 }
4118
4119 // End frame
4120 g.WithinFrameScope = false;
4121 g.FrameCountEnded = g.FrameCount;
4122
4123 // Initiate moving window + handle left-click and right-click focus
4124 UpdateMouseMovingWindowEndFrame();
4125
4126 // Sort the window list so that all child windows are after their parent
4127 // We cannot do that on FocusWindow() because childs may not exist yet
4128 g.WindowsTempSortBuffer.resize(0);
4129 g.WindowsTempSortBuffer.reserve(g.Windows.Size);
4130 for (int i = 0; i != g.Windows.Size; i++)
4131 {
4132 ImGuiWindow* window = g.Windows[i];
4133 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4134 continue;
4135 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
4136 }
4137
4138 // 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.
4139 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
4140 g.Windows.swap(g.WindowsTempSortBuffer);
4141 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4142
4143 // Unlock font atlas
4144 g.IO.Fonts->Locked = false;
4145
4146 // Clear Input data for next frame
4147 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4148 g.IO.InputQueueCharacters.resize(0);
4149 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4150 }
4151
Render()4152 void ImGui::Render()
4153 {
4154 ImGuiContext& g = *GImGui;
4155 IM_ASSERT(g.Initialized);
4156
4157 if (g.FrameCountEnded != g.FrameCount)
4158 EndFrame();
4159 g.FrameCountRendered = g.FrameCount;
4160 g.IO.MetricsRenderWindows = 0;
4161 g.DrawDataBuilder.Clear();
4162
4163 // Add background ImDrawList
4164 if (!g.BackgroundDrawList.VtxBuffer.empty())
4165 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4166
4167 // Add ImDrawList to render
4168 ImGuiWindow* windows_to_render_top_most[2];
4169 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4170 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingList : NULL);
4171 for (int n = 0; n != g.Windows.Size; n++)
4172 {
4173 ImGuiWindow* window = g.Windows[n];
4174 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4175 AddRootWindowToDrawData(window);
4176 }
4177 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4178 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4179 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4180 g.DrawDataBuilder.FlattenIntoSingleLayer();
4181
4182 // Draw software mouse cursor if requested
4183 if (g.IO.MouseDrawCursor)
4184 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4185
4186 // Add foreground ImDrawList
4187 if (!g.ForegroundDrawList.VtxBuffer.empty())
4188 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4189
4190 // Setup ImDrawData structure for end-user
4191 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4192 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4193 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4194
4195 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4196 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4197 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4198 g.IO.RenderDrawListsFn(&g.DrawData);
4199 #endif
4200 }
4201
4202 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4203 // 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)4204 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4205 {
4206 ImGuiContext& g = *GImGui;
4207
4208 const char* text_display_end;
4209 if (hide_text_after_double_hash)
4210 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4211 else
4212 text_display_end = text_end;
4213
4214 ImFont* font = g.Font;
4215 const float font_size = g.FontSize;
4216 if (text == text_display_end)
4217 return ImVec2(0.0f, font_size);
4218 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4219
4220 // Round
4221 text_size.x = IM_FLOOR(text_size.x + 0.95f);
4222
4223 return text_size;
4224 }
4225
4226 // Find window given position, search front-to-back
4227 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
4228 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4229 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4230 static void FindHoveredWindow()
4231 {
4232 ImGuiContext& g = *GImGui;
4233
4234 ImGuiWindow* hovered_window = NULL;
4235 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4236 hovered_window = g.MovingWindow;
4237
4238 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4239 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;
4240 for (int i = g.Windows.Size - 1; i >= 0; i--)
4241 {
4242 ImGuiWindow* window = g.Windows[i];
4243 if (!window->Active || window->Hidden)
4244 continue;
4245 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4246 continue;
4247
4248 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4249 ImRect bb(window->OuterRectClipped);
4250 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4251 bb.Expand(padding_regular);
4252 else
4253 bb.Expand(padding_for_resize_from_edges);
4254 if (!bb.Contains(g.IO.MousePos))
4255 continue;
4256
4257 // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
4258 if (hovered_window == NULL)
4259 hovered_window = window;
4260 if (hovered_window)
4261 break;
4262 }
4263
4264 g.HoveredWindow = hovered_window;
4265 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4266
4267 }
4268
4269 // Test if mouse cursor is hovering given rectangle
4270 // NB- Rectangle is clipped by our current clip setting
4271 // 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)4272 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4273 {
4274 ImGuiContext& g = *GImGui;
4275
4276 // Clip
4277 ImRect rect_clipped(r_min, r_max);
4278 if (clip)
4279 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4280
4281 // Expand for touch input
4282 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4283 if (!rect_for_touch.Contains(g.IO.MousePos))
4284 return false;
4285 return true;
4286 }
4287
GetKeyIndex(ImGuiKey imgui_key)4288 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4289 {
4290 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4291 ImGuiContext& g = *GImGui;
4292 return g.IO.KeyMap[imgui_key];
4293 }
4294
4295 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
4296 // Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4297 bool ImGui::IsKeyDown(int user_key_index)
4298 {
4299 if (user_key_index < 0)
4300 return false;
4301 ImGuiContext& g = *GImGui;
4302 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4303 return g.IO.KeysDown[user_key_index];
4304 }
4305
4306 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4307 // t1 = current time (e.g.: g.Time)
4308 // An event is triggered at:
4309 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4310 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4311 {
4312 if (t1 == 0.0f)
4313 return 1;
4314 if (t0 >= t1)
4315 return 0;
4316 if (repeat_rate <= 0.0f)
4317 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4318 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4319 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4320 const int count = count_t1 - count_t0;
4321 return count;
4322 }
4323
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4324 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4325 {
4326 ImGuiContext& g = *GImGui;
4327 if (key_index < 0)
4328 return 0;
4329 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4330 const float t = g.IO.KeysDownDuration[key_index];
4331 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4332 }
4333
IsKeyPressed(int user_key_index,bool repeat)4334 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4335 {
4336 ImGuiContext& g = *GImGui;
4337 if (user_key_index < 0)
4338 return false;
4339 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4340 const float t = g.IO.KeysDownDuration[user_key_index];
4341 if (t == 0.0f)
4342 return true;
4343 if (repeat && t > g.IO.KeyRepeatDelay)
4344 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4345 return false;
4346 }
4347
IsKeyReleased(int user_key_index)4348 bool ImGui::IsKeyReleased(int user_key_index)
4349 {
4350 ImGuiContext& g = *GImGui;
4351 if (user_key_index < 0) return false;
4352 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4353 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4354 }
4355
IsMouseDown(ImGuiMouseButton button)4356 bool ImGui::IsMouseDown(ImGuiMouseButton button)
4357 {
4358 ImGuiContext& g = *GImGui;
4359 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4360 return g.IO.MouseDown[button];
4361 }
4362
IsMouseClicked(ImGuiMouseButton button,bool repeat)4363 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
4364 {
4365 ImGuiContext& g = *GImGui;
4366 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4367 const float t = g.IO.MouseDownDuration[button];
4368 if (t == 0.0f)
4369 return true;
4370
4371 if (repeat && t > g.IO.KeyRepeatDelay)
4372 {
4373 // 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.
4374 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4375 if (amount > 0)
4376 return true;
4377 }
4378 return false;
4379 }
4380
IsMouseReleased(ImGuiMouseButton button)4381 bool ImGui::IsMouseReleased(ImGuiMouseButton button)
4382 {
4383 ImGuiContext& g = *GImGui;
4384 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4385 return g.IO.MouseReleased[button];
4386 }
4387
IsMouseDoubleClicked(ImGuiMouseButton button)4388 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
4389 {
4390 ImGuiContext& g = *GImGui;
4391 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4392 return g.IO.MouseDoubleClicked[button];
4393 }
4394
4395 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(ImGuiMouseButton button,float lock_threshold)4396 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
4397 {
4398 ImGuiContext& g = *GImGui;
4399 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4400 if (lock_threshold < 0.0f)
4401 lock_threshold = g.IO.MouseDragThreshold;
4402 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4403 }
4404
IsMouseDragging(ImGuiMouseButton button,float lock_threshold)4405 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
4406 {
4407 ImGuiContext& g = *GImGui;
4408 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4409 if (!g.IO.MouseDown[button])
4410 return false;
4411 return IsMouseDragPastThreshold(button, lock_threshold);
4412 }
4413
GetMousePos()4414 ImVec2 ImGui::GetMousePos()
4415 {
4416 ImGuiContext& g = *GImGui;
4417 return g.IO.MousePos;
4418 }
4419
4420 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4421 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4422 {
4423 ImGuiContext& g = *GImGui;
4424 if (g.BeginPopupStack.Size > 0)
4425 return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4426 return g.IO.MousePos;
4427 }
4428
4429 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4430 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4431 {
4432 // The assert is only to silence a false-positive in XCode Static Analysis.
4433 // 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).
4434 IM_ASSERT(GImGui != NULL);
4435 const float MOUSE_INVALID = -256000.0f;
4436 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4437 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4438 }
4439
IsAnyMouseDown()4440 bool ImGui::IsAnyMouseDown()
4441 {
4442 ImGuiContext& g = *GImGui;
4443 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4444 if (g.IO.MouseDown[n])
4445 return true;
4446 return false;
4447 }
4448
4449 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4450 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4451 // 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)4452 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
4453 {
4454 ImGuiContext& g = *GImGui;
4455 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4456 if (lock_threshold < 0.0f)
4457 lock_threshold = g.IO.MouseDragThreshold;
4458 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4459 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4460 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4461 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4462 return ImVec2(0.0f, 0.0f);
4463 }
4464
ResetMouseDragDelta(ImGuiMouseButton button)4465 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
4466 {
4467 ImGuiContext& g = *GImGui;
4468 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4469 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4470 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4471 }
4472
GetMouseCursor()4473 ImGuiMouseCursor ImGui::GetMouseCursor()
4474 {
4475 return GImGui->MouseCursor;
4476 }
4477
SetMouseCursor(ImGuiMouseCursor cursor_type)4478 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4479 {
4480 GImGui->MouseCursor = cursor_type;
4481 }
4482
CaptureKeyboardFromApp(bool capture)4483 void ImGui::CaptureKeyboardFromApp(bool capture)
4484 {
4485 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4486 }
4487
CaptureMouseFromApp(bool capture)4488 void ImGui::CaptureMouseFromApp(bool capture)
4489 {
4490 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4491 }
4492
IsItemActive()4493 bool ImGui::IsItemActive()
4494 {
4495 ImGuiContext& g = *GImGui;
4496 if (g.ActiveId)
4497 {
4498 ImGuiWindow* window = g.CurrentWindow;
4499 return g.ActiveId == window->DC.LastItemId;
4500 }
4501 return false;
4502 }
4503
IsItemActivated()4504 bool ImGui::IsItemActivated()
4505 {
4506 ImGuiContext& g = *GImGui;
4507 if (g.ActiveId)
4508 {
4509 ImGuiWindow* window = g.CurrentWindow;
4510 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4511 return true;
4512 }
4513 return false;
4514 }
4515
IsItemDeactivated()4516 bool ImGui::IsItemDeactivated()
4517 {
4518 ImGuiContext& g = *GImGui;
4519 ImGuiWindow* window = g.CurrentWindow;
4520 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4521 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4522 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4523 }
4524
IsItemDeactivatedAfterEdit()4525 bool ImGui::IsItemDeactivatedAfterEdit()
4526 {
4527 ImGuiContext& g = *GImGui;
4528 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4529 }
4530
IsItemFocused()4531 bool ImGui::IsItemFocused()
4532 {
4533 ImGuiContext& g = *GImGui;
4534 ImGuiWindow* window = g.CurrentWindow;
4535
4536 if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4537 return false;
4538 return true;
4539 }
4540
IsItemClicked(ImGuiMouseButton mouse_button)4541 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
4542 {
4543 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4544 }
4545
IsItemToggledOpen()4546 bool ImGui::IsItemToggledOpen()
4547 {
4548 ImGuiContext& g = *GImGui;
4549 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4550 }
4551
IsItemToggledSelection()4552 bool ImGui::IsItemToggledSelection()
4553 {
4554 ImGuiContext& g = *GImGui;
4555 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4556 }
4557
IsAnyItemHovered()4558 bool ImGui::IsAnyItemHovered()
4559 {
4560 ImGuiContext& g = *GImGui;
4561 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4562 }
4563
IsAnyItemActive()4564 bool ImGui::IsAnyItemActive()
4565 {
4566 ImGuiContext& g = *GImGui;
4567 return g.ActiveId != 0;
4568 }
4569
IsAnyItemFocused()4570 bool ImGui::IsAnyItemFocused()
4571 {
4572 ImGuiContext& g = *GImGui;
4573 return g.NavId != 0 && !g.NavDisableHighlight;
4574 }
4575
IsItemVisible()4576 bool ImGui::IsItemVisible()
4577 {
4578 ImGuiWindow* window = GetCurrentWindowRead();
4579 return window->ClipRect.Overlaps(window->DC.LastItemRect);
4580 }
4581
IsItemEdited()4582 bool ImGui::IsItemEdited()
4583 {
4584 ImGuiWindow* window = GetCurrentWindowRead();
4585 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4586 }
4587
4588 // 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()4589 void ImGui::SetItemAllowOverlap()
4590 {
4591 ImGuiContext& g = *GImGui;
4592 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4593 g.HoveredIdAllowOverlap = true;
4594 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4595 g.ActiveIdAllowOverlap = true;
4596 }
4597
GetItemRectMin()4598 ImVec2 ImGui::GetItemRectMin()
4599 {
4600 ImGuiWindow* window = GetCurrentWindowRead();
4601 return window->DC.LastItemRect.Min;
4602 }
4603
GetItemRectMax()4604 ImVec2 ImGui::GetItemRectMax()
4605 {
4606 ImGuiWindow* window = GetCurrentWindowRead();
4607 return window->DC.LastItemRect.Max;
4608 }
4609
GetItemRectSize()4610 ImVec2 ImGui::GetItemRectSize()
4611 {
4612 ImGuiWindow* window = GetCurrentWindowRead();
4613 return window->DC.LastItemRect.GetSize();
4614 }
4615
GetViewportRect()4616 static ImRect GetViewportRect()
4617 {
4618 ImGuiContext& g = *GImGui;
4619 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4620 }
4621
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4622 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4623 {
4624 ImGuiContext& g = *GImGui;
4625 ImGuiWindow* parent_window = g.CurrentWindow;
4626
4627 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4628 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4629
4630 // Size
4631 const ImVec2 content_avail = GetContentRegionAvail();
4632 ImVec2 size = ImFloor(size_arg);
4633 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4634 if (size.x <= 0.0f)
4635 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4636 if (size.y <= 0.0f)
4637 size.y = ImMax(content_avail.y + size.y, 4.0f);
4638 SetNextWindowSize(size);
4639
4640 // 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.
4641 char title[256];
4642 if (name)
4643 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4644 else
4645 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4646
4647 const float backup_border_size = g.Style.ChildBorderSize;
4648 if (!border)
4649 g.Style.ChildBorderSize = 0.0f;
4650 bool ret = Begin(title, NULL, flags);
4651 g.Style.ChildBorderSize = backup_border_size;
4652
4653 ImGuiWindow* child_window = g.CurrentWindow;
4654 child_window->ChildId = id;
4655 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4656
4657 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4658 // While this is not really documented/defined, it seems that the expected thing to do.
4659 if (child_window->BeginCount == 1)
4660 parent_window->DC.CursorPos = child_window->Pos;
4661
4662 // Process navigation-in immediately so NavInit can run on first frame
4663 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4664 {
4665 FocusWindow(child_window);
4666 NavInitWindow(child_window, false);
4667 SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4668 g.ActiveIdSource = ImGuiInputSource_Nav;
4669 }
4670 return ret;
4671 }
4672
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4673 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4674 {
4675 ImGuiWindow* window = GetCurrentWindow();
4676 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4677 }
4678
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4679 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4680 {
4681 IM_ASSERT(id != 0);
4682 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4683 }
4684
EndChild()4685 void ImGui::EndChild()
4686 {
4687 ImGuiContext& g = *GImGui;
4688 ImGuiWindow* window = g.CurrentWindow;
4689
4690 IM_ASSERT(g.WithinEndChild == false);
4691 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls
4692
4693 g.WithinEndChild = true;
4694 if (window->BeginCount > 1)
4695 {
4696 End();
4697 }
4698 else
4699 {
4700 ImVec2 sz = window->Size;
4701 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4702 sz.x = ImMax(4.0f, sz.x);
4703 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4704 sz.y = ImMax(4.0f, sz.y);
4705 End();
4706
4707 ImGuiWindow* parent_window = g.CurrentWindow;
4708 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4709 ItemSize(sz);
4710 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4711 {
4712 ItemAdd(bb, window->ChildId);
4713 RenderNavHighlight(bb, window->ChildId);
4714
4715 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4716 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4717 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4718 }
4719 else
4720 {
4721 // Not navigable into
4722 ItemAdd(bb, 0);
4723 }
4724 }
4725 g.WithinEndChild = false;
4726 }
4727
4728 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4729 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4730 {
4731 ImGuiContext& g = *GImGui;
4732 const ImGuiStyle& style = g.Style;
4733 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4734 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4735 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4736 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4737 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4738 PopStyleVar(3);
4739 PopStyleColor();
4740 return ret;
4741 }
4742
EndChildFrame()4743 void ImGui::EndChildFrame()
4744 {
4745 EndChild();
4746 }
4747
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4748 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4749 {
4750 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4751 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4752 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4753 }
4754
FindWindowByID(ImGuiID id)4755 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4756 {
4757 ImGuiContext& g = *GImGui;
4758 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4759 }
4760
FindWindowByName(const char * name)4761 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4762 {
4763 ImGuiID id = ImHashStr(name);
4764 return FindWindowByID(id);
4765 }
4766
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4767 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4768 {
4769 ImGuiContext& g = *GImGui;
4770 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4771
4772 // Create window the first time
4773 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4774 window->Flags = flags;
4775 g.WindowsById.SetVoidPtr(window->ID, window);
4776
4777 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4778 window->Pos = ImVec2(60, 60);
4779
4780 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4781 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4782 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4783 {
4784 // Retrieve settings from .ini file
4785 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4786 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4787 window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
4788 window->Collapsed = settings->Collapsed;
4789 if (settings->Size.x > 0 && settings->Size.y > 0)
4790 size = ImVec2(settings->Size.x, settings->Size.y);
4791 }
4792 window->Size = window->SizeFull = ImFloor(size);
4793 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4794
4795 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4796 {
4797 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4798 window->AutoFitOnlyGrows = false;
4799 }
4800 else
4801 {
4802 if (window->Size.x <= 0.0f)
4803 window->AutoFitFramesX = 2;
4804 if (window->Size.y <= 0.0f)
4805 window->AutoFitFramesY = 2;
4806 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4807 }
4808
4809 g.WindowsFocusOrder.push_back(window);
4810 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4811 g.Windows.push_front(window); // Quite slow but rare and only once
4812 else
4813 g.Windows.push_back(window);
4814 return window;
4815 }
4816
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4817 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4818 {
4819 ImGuiContext& g = *GImGui;
4820 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4821 {
4822 // Using -1,-1 on either X/Y axis to preserve the current size.
4823 ImRect cr = g.NextWindowData.SizeConstraintRect;
4824 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4825 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4826 if (g.NextWindowData.SizeCallback)
4827 {
4828 ImGuiSizeCallbackData data;
4829 data.UserData = g.NextWindowData.SizeCallbackUserData;
4830 data.Pos = window->Pos;
4831 data.CurrentSize = window->SizeFull;
4832 data.DesiredSize = new_size;
4833 g.NextWindowData.SizeCallback(&data);
4834 new_size = data.DesiredSize;
4835 }
4836 new_size.x = IM_FLOOR(new_size.x);
4837 new_size.y = IM_FLOOR(new_size.y);
4838 }
4839
4840 // Minimum size
4841 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4842 {
4843 ImGuiWindow* window_for_height = window;
4844 new_size = ImMax(new_size, g.Style.WindowMinSize);
4845 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
4846 }
4847 return new_size;
4848 }
4849
CalcWindowContentSize(ImGuiWindow * window)4850 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4851 {
4852 if (window->Collapsed)
4853 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4854 return window->ContentSize;
4855 if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4856 return window->ContentSize;
4857
4858 ImVec2 sz;
4859 sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4860 sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4861 return sz;
4862 }
4863
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4864 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4865 {
4866 ImGuiContext& g = *GImGui;
4867 ImGuiStyle& style = g.Style;
4868 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4869 ImVec2 size_pad = window->WindowPadding * 2.0f;
4870 ImVec2 size_desired = size_contents + size_pad + size_decorations;
4871 if (window->Flags & ImGuiWindowFlags_Tooltip)
4872 {
4873 // Tooltip always resize
4874 return size_desired;
4875 }
4876 else
4877 {
4878 // Maximum window size is determined by the viewport size or monitor size
4879 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4880 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4881 ImVec2 size_min = style.WindowMinSize;
4882 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)
4883 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4884 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4885
4886 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4887 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4888 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4889 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);
4890 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);
4891 if (will_have_scrollbar_x)
4892 size_auto_fit.y += style.ScrollbarSize;
4893 if (will_have_scrollbar_y)
4894 size_auto_fit.x += style.ScrollbarSize;
4895 return size_auto_fit;
4896 }
4897 }
4898
CalcWindowExpectedSize(ImGuiWindow * window)4899 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4900 {
4901 ImVec2 size_contents = CalcWindowContentSize(window);
4902 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4903 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4904 return size_final;
4905 }
4906
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4907 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4908 {
4909 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4910 return ImGuiCol_PopupBg;
4911 if (flags & ImGuiWindowFlags_ChildWindow)
4912 return ImGuiCol_ChildBg;
4913 return ImGuiCol_WindowBg;
4914 }
4915
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4916 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4917 {
4918 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
4919 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4920 ImVec2 size_expected = pos_max - pos_min;
4921 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
4922 *out_pos = pos_min;
4923 if (corner_norm.x == 0.0f)
4924 out_pos->x -= (size_constrained.x - size_expected.x);
4925 if (corner_norm.y == 0.0f)
4926 out_pos->y -= (size_constrained.y - size_expected.y);
4927 *out_size = size_constrained;
4928 }
4929
4930 struct ImGuiResizeGripDef
4931 {
4932 ImVec2 CornerPosN;
4933 ImVec2 InnerDir;
4934 int AngleMin12, AngleMax12;
4935 };
4936
4937 static const ImGuiResizeGripDef resize_grip_def[4] =
4938 {
4939 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower-right
4940 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower-left
4941 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper-left (Unused)
4942 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper-right (Unused)
4943 };
4944
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4945 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4946 {
4947 ImRect rect = window->Rect();
4948 if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4949 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
4950 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
4951 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
4952 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
4953 IM_ASSERT(0);
4954 return ImRect();
4955 }
4956
4957 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
4958 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)4959 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
4960 {
4961 IM_ASSERT(n >= 0 && n <= 7);
4962 ImGuiID id = window->ID;
4963 id = ImHashStr("#RESIZE", 0, id);
4964 id = ImHashData(&n, sizeof(int), id);
4965 return id;
4966 }
4967
4968 // Handle resize for: Resize Grips, Borders, Gamepad
4969 // Return true when using auto-fit (double click on resize grip)
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4970 static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4971 {
4972 ImGuiContext& g = *GImGui;
4973 ImGuiWindowFlags flags = window->Flags;
4974
4975 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4976 return false;
4977 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4978 return false;
4979
4980 bool ret_auto_fit = false;
4981 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4982 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
4983 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
4984 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4985
4986 ImVec2 pos_target(FLT_MAX, FLT_MAX);
4987 ImVec2 size_target(FLT_MAX, FLT_MAX);
4988
4989 // Resize grips and borders are on layer 1
4990 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
4991 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
4992
4993 // Manual resize grips
4994 PushID("#RESIZE");
4995 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4996 {
4997 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4998 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4999
5000 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
5001 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
5002 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
5003 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
5004 bool hovered, held;
5005 ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
5006 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
5007 if (hovered || held)
5008 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
5009
5010 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
5011 {
5012 // Manual auto-fit when double-clicking
5013 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
5014 ret_auto_fit = true;
5015 ClearActiveID();
5016 }
5017 else if (held)
5018 {
5019 // Resize from any of the four corners
5020 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
5021 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
5022 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
5023 }
5024 if (resize_grip_n == 0 || held || hovered)
5025 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
5026 }
5027 for (int border_n = 0; border_n < resize_border_count; border_n++)
5028 {
5029 bool hovered, held;
5030 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
5031 ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
5032 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
5033 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
5034 {
5035 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
5036 if (held)
5037 *border_held = border_n;
5038 }
5039 if (held)
5040 {
5041 ImVec2 border_target = window->Pos;
5042 ImVec2 border_posn;
5043 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
5044 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
5045 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
5046 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
5047 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
5048 }
5049 }
5050 PopID();
5051
5052 // Restore nav layer
5053 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5054 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5055
5056 // Navigation resize (keyboard/gamepad)
5057 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
5058 {
5059 ImVec2 nav_resize_delta;
5060 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
5061 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
5062 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
5063 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
5064 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
5065 {
5066 const float NAV_RESIZE_SPEED = 600.0f;
5067 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
5068 g.NavWindowingToggleLayer = false;
5069 g.NavDisableMouseHover = true;
5070 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
5071 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
5072 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
5073 }
5074 }
5075
5076 // Apply back modified position/size to window
5077 if (size_target.x != FLT_MAX)
5078 {
5079 window->SizeFull = size_target;
5080 MarkIniSettingsDirty(window);
5081 }
5082 if (pos_target.x != FLT_MAX)
5083 {
5084 window->Pos = ImFloor(pos_target);
5085 MarkIniSettingsDirty(window);
5086 }
5087
5088 window->Size = window->SizeFull;
5089 return ret_auto_fit;
5090 }
5091
ClampWindowRect(ImGuiWindow * window,const ImRect & rect,const ImVec2 & padding)5092 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
5093 {
5094 ImGuiContext& g = *GImGui;
5095 ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5096 window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
5097 }
5098
RenderWindowOuterBorders(ImGuiWindow * window)5099 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5100 {
5101 ImGuiContext& g = *GImGui;
5102 float rounding = window->WindowRounding;
5103 float border_size = window->WindowBorderSize;
5104 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5105 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5106
5107 int border_held = window->ResizeBorderHeld;
5108 if (border_held != -1)
5109 {
5110 struct ImGuiResizeBorderDef
5111 {
5112 ImVec2 InnerDir;
5113 ImVec2 CornerPosN1, CornerPosN2;
5114 float OuterAngle;
5115 };
5116 static const ImGuiResizeBorderDef resize_border_def[4] =
5117 {
5118 { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
5119 { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
5120 { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
5121 { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left
5122 };
5123 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5124 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5125 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);
5126 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);
5127 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5128 }
5129 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5130 {
5131 float y = window->Pos.y + window->TitleBarHeight() - 1;
5132 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);
5133 }
5134 }
5135
5136 // Draw background and borders
5137 // 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)5138 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)
5139 {
5140 ImGuiContext& g = *GImGui;
5141 ImGuiStyle& style = g.Style;
5142 ImGuiWindowFlags flags = window->Flags;
5143
5144 // Ensure that ScrollBar doesn't read last frame's SkipItems
5145 window->SkipItems = false;
5146
5147 // Draw window + handle manual resize
5148 // 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.
5149 const float window_rounding = window->WindowRounding;
5150 const float window_border_size = window->WindowBorderSize;
5151 if (window->Collapsed)
5152 {
5153 // Title bar only
5154 float backup_border_size = style.FrameBorderSize;
5155 g.Style.FrameBorderSize = window->WindowBorderSize;
5156 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5157 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5158 g.Style.FrameBorderSize = backup_border_size;
5159 }
5160 else
5161 {
5162 // Window background
5163 if (!(flags & ImGuiWindowFlags_NoBackground))
5164 {
5165 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5166 bool override_alpha = false;
5167 float alpha = 1.0f;
5168 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5169 {
5170 alpha = g.NextWindowData.BgAlphaVal;
5171 override_alpha = true;
5172 }
5173 if (override_alpha)
5174 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5175 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5176 }
5177
5178 // Title bar
5179 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5180 {
5181 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5182 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5183 }
5184
5185 // Menu bar
5186 if (flags & ImGuiWindowFlags_MenuBar)
5187 {
5188 ImRect menu_bar_rect = window->MenuBarRect();
5189 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.
5190 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);
5191 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5192 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5193 }
5194
5195 // Scrollbars
5196 if (window->ScrollbarX)
5197 Scrollbar(ImGuiAxis_X);
5198 if (window->ScrollbarY)
5199 Scrollbar(ImGuiAxis_Y);
5200
5201 // Render resize grips (after their input handling so we don't have a frame of latency)
5202 if (!(flags & ImGuiWindowFlags_NoResize))
5203 {
5204 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5205 {
5206 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5207 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5208 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)));
5209 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)));
5210 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);
5211 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5212 }
5213 }
5214
5215 // Borders
5216 RenderWindowOuterBorders(window);
5217 }
5218 }
5219
5220 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5221 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5222 {
5223 ImGuiContext& g = *GImGui;
5224 ImGuiStyle& style = g.Style;
5225 ImGuiWindowFlags flags = window->Flags;
5226
5227 const bool has_close_button = (p_open != NULL);
5228 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5229
5230 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5231 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5232 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5233 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5234 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5235
5236 // Layout buttons
5237 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5238 float pad_l = style.FramePadding.x;
5239 float pad_r = style.FramePadding.x;
5240 float button_sz = g.FontSize;
5241 ImVec2 close_button_pos;
5242 ImVec2 collapse_button_pos;
5243 if (has_close_button)
5244 {
5245 pad_r += button_sz;
5246 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5247 }
5248 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5249 {
5250 pad_r += button_sz;
5251 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5252 }
5253 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5254 {
5255 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5256 pad_l += button_sz;
5257 }
5258
5259 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5260 if (has_collapse_button)
5261 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5262 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5263
5264 // Close button
5265 if (has_close_button)
5266 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5267 *p_open = false;
5268
5269 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5270 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5271 window->DC.ItemFlags = item_flags_backup;
5272
5273 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5274 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5275 const char* UNSAVED_DOCUMENT_MARKER = "*";
5276 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5277 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5278
5279 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5280 // while uncentered title text will still reach edges correct.
5281 if (pad_l > style.FramePadding.x)
5282 pad_l += g.Style.ItemInnerSpacing.x;
5283 if (pad_r > style.FramePadding.x)
5284 pad_r += g.Style.ItemInnerSpacing.x;
5285 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5286 {
5287 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5288 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5289 pad_l = ImMax(pad_l, pad_extend * centerness);
5290 pad_r = ImMax(pad_r, pad_extend * centerness);
5291 }
5292
5293 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);
5294 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5295 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5296 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5297 if (flags & ImGuiWindowFlags_UnsavedDocument)
5298 {
5299 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);
5300 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5301 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5302 }
5303 }
5304
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5305 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5306 {
5307 window->ParentWindow = parent_window;
5308 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5309 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5310 window->RootWindow = parent_window->RootWindow;
5311 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5312 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5313 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5314 {
5315 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5316 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5317 }
5318 }
5319
5320 // Push a new Dear ImGui window to add widgets to.
5321 // - 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.
5322 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5323 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5324 // 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.
5325 // - 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.
5326 // - 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)5327 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5328 {
5329 ImGuiContext& g = *GImGui;
5330 const ImGuiStyle& style = g.Style;
5331 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5332 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
5333 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5334
5335 // Find or create
5336 ImGuiWindow* window = FindWindowByName(name);
5337 const bool window_just_created = (window == NULL);
5338 if (window_just_created)
5339 {
5340 ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
5341 window = CreateNewWindow(name, size_on_first_use, flags);
5342 }
5343
5344 // Automatically disable manual moving/resizing when NoInputs is set
5345 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5346 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5347
5348 if (flags & ImGuiWindowFlags_NavFlattened)
5349 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5350
5351 const int current_frame = g.FrameCount;
5352 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5353 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
5354
5355 // Update the Appearing flag
5356 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5357 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5358 if (flags & ImGuiWindowFlags_Popup)
5359 {
5360 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5361 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5362 window_just_activated_by_user |= (window != popup_ref.Window);
5363 }
5364 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5365 if (window->Appearing)
5366 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5367
5368 // Update Flags, LastFrameActive, BeginOrderXXX fields
5369 if (first_begin_of_the_frame)
5370 {
5371 window->Flags = (ImGuiWindowFlags)flags;
5372 window->LastFrameActive = current_frame;
5373 window->LastTimeActive = (float)g.Time;
5374 window->BeginOrderWithinParent = 0;
5375 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5376 }
5377 else
5378 {
5379 flags = window->Flags;
5380 }
5381
5382 // 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
5383 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5384 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5385 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5386
5387 // We allow window memory to be compacted so recreate the base stack when needed.
5388 if (window->IDStack.Size == 0)
5389 window->IDStack.push_back(window->ID);
5390
5391 // Add to stack
5392 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5393 g.CurrentWindowStack.push_back(window);
5394 g.CurrentWindow = NULL;
5395 ErrorCheckBeginEndCompareStacksSize(window, true);
5396 if (flags & ImGuiWindowFlags_Popup)
5397 {
5398 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5399 popup_ref.Window = window;
5400 g.BeginPopupStack.push_back(popup_ref);
5401 window->PopupId = popup_ref.PopupId;
5402 }
5403
5404 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5405 window->NavLastIds[0] = 0;
5406
5407 // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
5408 if (first_begin_of_the_frame)
5409 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5410
5411 // Process SetNextWindow***() calls
5412 bool window_pos_set_by_api = false;
5413 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5414 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5415 {
5416 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5417 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5418 {
5419 // May be processed on the next frame if this is our first frame and we are measuring size
5420 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5421 window->SetWindowPosVal = g.NextWindowData.PosVal;
5422 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5423 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5424 }
5425 else
5426 {
5427 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5428 }
5429 }
5430 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5431 {
5432 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5433 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5434 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5435 }
5436 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5437 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5438 else if (first_begin_of_the_frame)
5439 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5440 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5441 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5442 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5443 FocusWindow(window);
5444 if (window->Appearing)
5445 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5446
5447 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5448 if (first_begin_of_the_frame)
5449 {
5450 // Initialize
5451 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5452 window->Active = true;
5453 window->HasCloseButton = (p_open != NULL);
5454 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5455 window->IDStack.resize(1);
5456
5457 // Restore buffer capacity when woken from a compacted state, to avoid
5458 if (window->MemoryCompacted)
5459 GcAwakeTransientWindowBuffers(window);
5460
5461 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5462 // 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.
5463 bool window_title_visible_elsewhere = false;
5464 if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5465 window_title_visible_elsewhere = true;
5466 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5467 {
5468 size_t buf_len = (size_t)window->NameBufLen;
5469 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5470 window->NameBufLen = (int)buf_len;
5471 }
5472
5473 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5474
5475 // Update contents size from last frame for auto-fitting (or use explicit size)
5476 window->ContentSize = CalcWindowContentSize(window);
5477 if (window->HiddenFramesCanSkipItems > 0)
5478 window->HiddenFramesCanSkipItems--;
5479 if (window->HiddenFramesCannotSkipItems > 0)
5480 window->HiddenFramesCannotSkipItems--;
5481
5482 // Hide new windows for one frame until they calculate their size
5483 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5484 window->HiddenFramesCannotSkipItems = 1;
5485
5486 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5487 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5488 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5489 {
5490 window->HiddenFramesCannotSkipItems = 1;
5491 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5492 {
5493 if (!window_size_x_set_by_api)
5494 window->Size.x = window->SizeFull.x = 0.f;
5495 if (!window_size_y_set_by_api)
5496 window->Size.y = window->SizeFull.y = 0.f;
5497 window->ContentSize = ImVec2(0.f, 0.f);
5498 }
5499 }
5500
5501 // SELECT VIEWPORT
5502 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5503 SetCurrentWindow(window);
5504
5505 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5506
5507 if (flags & ImGuiWindowFlags_ChildWindow)
5508 window->WindowBorderSize = style.ChildBorderSize;
5509 else
5510 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5511 window->WindowPadding = style.WindowPadding;
5512 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5513 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5514
5515 // Collapse window by double-clicking on title bar
5516 // 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
5517 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5518 {
5519 // 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.
5520 ImRect title_bar_rect = window->TitleBarRect();
5521 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5522 window->WantCollapseToggle = true;
5523 if (window->WantCollapseToggle)
5524 {
5525 window->Collapsed = !window->Collapsed;
5526 MarkIniSettingsDirty(window);
5527 FocusWindow(window);
5528 }
5529 }
5530 else
5531 {
5532 window->Collapsed = false;
5533 }
5534 window->WantCollapseToggle = false;
5535
5536 // SIZE
5537
5538 // Calculate auto-fit size, handle automatic resize
5539 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5540 bool use_current_size_for_scrollbar_x = window_just_created;
5541 bool use_current_size_for_scrollbar_y = window_just_created;
5542 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5543 {
5544 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5545 if (!window_size_x_set_by_api)
5546 {
5547 window->SizeFull.x = size_auto_fit.x;
5548 use_current_size_for_scrollbar_x = true;
5549 }
5550 if (!window_size_y_set_by_api)
5551 {
5552 window->SizeFull.y = size_auto_fit.y;
5553 use_current_size_for_scrollbar_y = true;
5554 }
5555 }
5556 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5557 {
5558 // Auto-fit may only grow window during the first few frames
5559 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5560 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5561 {
5562 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5563 use_current_size_for_scrollbar_x = true;
5564 }
5565 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5566 {
5567 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5568 use_current_size_for_scrollbar_y = true;
5569 }
5570 if (!window->Collapsed)
5571 MarkIniSettingsDirty(window);
5572 }
5573
5574 // Apply minimum/maximum window size constraints and final size
5575 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5576 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5577
5578 // Decoration size
5579 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5580
5581 // POSITION
5582
5583 // Popup latch its initial position, will position itself when it appears next frame
5584 if (window_just_activated_by_user)
5585 {
5586 window->AutoPosLastDirection = ImGuiDir_None;
5587 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5588 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5589 }
5590
5591 // Position child window
5592 if (flags & ImGuiWindowFlags_ChildWindow)
5593 {
5594 IM_ASSERT(parent_window && parent_window->Active);
5595 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5596 parent_window->DC.ChildWindows.push_back(window);
5597 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5598 window->Pos = parent_window->DC.CursorPos;
5599 }
5600
5601 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5602 if (window_pos_with_pivot)
5603 SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5604 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5605 window->Pos = FindBestWindowPosForPopup(window);
5606 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5607 window->Pos = FindBestWindowPosForPopup(window);
5608 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5609 window->Pos = FindBestWindowPosForPopup(window);
5610
5611 // Clamp position/size so window stays visible within its viewport or monitor
5612 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5613 ImRect viewport_rect(GetViewportRect());
5614 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5615 {
5616 ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5617 if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f)
5618 {
5619 ClampWindowRect(window, viewport_rect, clamp_padding);
5620 }
5621 }
5622 window->Pos = ImFloor(window->Pos);
5623
5624 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5625 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5626
5627 // Apply window focus (new and reactivated windows are moved to front)
5628 bool want_focus = false;
5629 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5630 {
5631 if (flags & ImGuiWindowFlags_Popup)
5632 want_focus = true;
5633 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5634 want_focus = true;
5635 }
5636
5637 // Handle manual resize: Resize Grips, Borders, Gamepad
5638 int border_held = -1;
5639 ImU32 resize_grip_col[4] = {};
5640 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5641 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5642 if (!window->Collapsed)
5643 if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
5644 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5645 window->ResizeBorderHeld = (signed char)border_held;
5646
5647 // SCROLLBAR VISIBILITY
5648
5649 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5650 if (!window->Collapsed)
5651 {
5652 // When reading the current size we need to read it after size constraints have been applied.
5653 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5654 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5655 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5656 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5657 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5658 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5659 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5660 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5661 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));
5662 if (window->ScrollbarX && !window->ScrollbarY)
5663 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5664 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5665 }
5666
5667 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5668 // Update various regions. Variables they depends on should be set above in this function.
5669 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5670
5671 // Outer rectangle
5672 // Not affected by window border size. Used by:
5673 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5674 // - Begin() initial clipping rect for drawing window background and borders.
5675 // - Begin() clipping whole child
5676 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5677 const ImRect outer_rect = window->Rect();
5678 const ImRect title_bar_rect = window->TitleBarRect();
5679 window->OuterRectClipped = outer_rect;
5680 window->OuterRectClipped.ClipWith(host_rect);
5681
5682 // Inner rectangle
5683 // Not affected by window border size. Used by:
5684 // - InnerClipRect
5685 // - ScrollToBringRectIntoView()
5686 // - NavUpdatePageUpPageDown()
5687 // - Scrollbar()
5688 window->InnerRect.Min.x = window->Pos.x;
5689 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5690 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5691 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5692
5693 // Inner clipping rectangle.
5694 // Will extend a little bit outside the normal work region.
5695 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5696 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5697 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5698 // Affected by window/frame border size. Used by:
5699 // - Begin() initial clip rect
5700 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5701 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5702 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5703 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5704 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5705 window->InnerClipRect.ClipWithFull(host_rect);
5706
5707 // Default item width. Make it proportional to window size if window manually resizes
5708 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5709 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5710 else
5711 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5712
5713 // SCROLLING
5714
5715 // Lock down maximum scrolling
5716 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5717 // for right/bottom aligned items without creating a scrollbar.
5718 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5719 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5720
5721 // Apply scrolling
5722 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5723 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5724
5725 // DRAWING
5726
5727 // Setup draw list and outer clipping rectangle
5728 window->DrawList->Clear();
5729 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5730 PushClipRect(host_rect.Min, host_rect.Max, false);
5731
5732 // Draw modal window background (darkens what is behind them, all viewports)
5733 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5734 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5735 if (dim_bg_for_modal || dim_bg_for_window_list)
5736 {
5737 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5738 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5739 }
5740
5741 // Draw navigation selection/windowing rectangle background
5742 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5743 {
5744 ImRect bb = window->Rect();
5745 bb.Expand(g.FontSize);
5746 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5747 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5748 }
5749
5750 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5751 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5752 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5753 // We also disabled this when we have dimming overlay behind this specific one child.
5754 // 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.
5755 {
5756 bool render_decorations_in_parent = false;
5757 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5758 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5759 render_decorations_in_parent = true;
5760 if (render_decorations_in_parent)
5761 window->DrawList = parent_window->DrawList;
5762
5763 // Handle title bar, scrollbar, resize grips and resize borders
5764 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5765 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5766 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5767
5768 if (render_decorations_in_parent)
5769 window->DrawList = &window->DrawListInst;
5770 }
5771
5772 // Draw navigation selection/windowing rectangle border
5773 if (g.NavWindowingTargetAnim == window)
5774 {
5775 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5776 ImRect bb = window->Rect();
5777 bb.Expand(g.FontSize);
5778 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5779 {
5780 bb.Expand(-g.FontSize - 1.0f);
5781 rounding = window->WindowRounding;
5782 }
5783 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5784 }
5785
5786 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5787
5788 // Work rectangle.
5789 // Affected by window padding and border size. Used by:
5790 // - Columns() for right-most edge
5791 // - TreeNode(), CollapsingHeader() for right-most edge
5792 // - BeginTabBar() for right-most edge
5793 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5794 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5795 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));
5796 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));
5797 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5798 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5799 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5800 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5801
5802 // [LEGACY] Content Region
5803 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5804 // Used by:
5805 // - Mouse wheel scrolling + many other things
5806 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5807 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5808 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));
5809 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));
5810
5811 // Setup drawing context
5812 // (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.)
5813 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5814 window->DC.GroupOffset.x = 0.0f;
5815 window->DC.ColumnsOffset.x = 0.0f;
5816 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5817 window->DC.CursorPos = window->DC.CursorStartPos;
5818 window->DC.CursorPosPrevLine = window->DC.CursorPos;
5819 window->DC.CursorMaxPos = window->DC.CursorStartPos;
5820 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5821 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5822
5823 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5824 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5825 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5826 window->DC.NavLayerActiveMaskNext = 0x00;
5827 window->DC.NavFocusScopeIdCurrent = 0;
5828 window->DC.NavHideHighlightOneFrame = false;
5829 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5830
5831 window->DC.MenuBarAppending = false;
5832 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5833 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5834 window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5835 window->DC.TreeDepth = 0;
5836 window->DC.TreeJumpToParentOnPopMask = 0x00;
5837 window->DC.ChildWindows.resize(0);
5838 window->DC.StateStorage = &window->StateStorage;
5839 window->DC.CurrentColumns = NULL;
5840 window->DC.LayoutType = ImGuiLayoutType_Vertical;
5841 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5842 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1;
5843
5844 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5845 window->DC.ItemWidth = window->ItemWidthDefault;
5846 window->DC.TextWrapPos = -1.0f; // disabled
5847 window->DC.ItemFlagsStack.resize(0);
5848 window->DC.ItemWidthStack.resize(0);
5849 window->DC.TextWrapPosStack.resize(0);
5850 window->DC.GroupStack.resize(0);
5851
5852 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5853 {
5854 window->DC.ItemFlags = parent_window->DC.ItemFlags;
5855 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5856 }
5857
5858 if (window->AutoFitFramesX > 0)
5859 window->AutoFitFramesX--;
5860 if (window->AutoFitFramesY > 0)
5861 window->AutoFitFramesY--;
5862
5863 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5864 if (want_focus)
5865 {
5866 FocusWindow(window);
5867 NavInitWindow(window, false);
5868 }
5869
5870 // Title bar
5871 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5872 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5873
5874 // Pressing CTRL+C while holding on a window copy its content to the clipboard
5875 // 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.
5876 // Maybe we can support CTRL+C on every element?
5877 /*
5878 if (g.ActiveId == move_id)
5879 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5880 LogToClipboard();
5881 */
5882
5883 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5884 // This is useful to allow creating context menus on title bar only, etc.
5885 window->DC.LastItemId = window->MoveId;
5886 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5887 window->DC.LastItemRect = title_bar_rect;
5888 #ifdef IMGUI_ENABLE_TEST_ENGINE
5889 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
5890 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
5891 #endif
5892 }
5893 else
5894 {
5895 // Append
5896 SetCurrentWindow(window);
5897 }
5898
5899 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5900
5901 // 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)
5902 if (first_begin_of_the_frame)
5903 window->WriteAccessed = false;
5904
5905 window->BeginCount++;
5906 g.NextWindowData.ClearFlags();
5907
5908 if (flags & ImGuiWindowFlags_ChildWindow)
5909 {
5910 // Child window can be out of sight and have "negative" clip windows.
5911 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5912 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5913 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5914 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5915 window->HiddenFramesCanSkipItems = 1;
5916
5917 // Hide along with parent or if parent is collapsed
5918 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
5919 window->HiddenFramesCanSkipItems = 1;
5920 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
5921 window->HiddenFramesCannotSkipItems = 1;
5922 }
5923
5924 // 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)
5925 if (style.Alpha <= 0.0f)
5926 window->HiddenFramesCanSkipItems = 1;
5927
5928 // Update the Hidden flag
5929 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
5930
5931 // Update the SkipItems flag, used to early out of all items functions (no layout required)
5932 bool skip_items = false;
5933 if (window->Collapsed || !window->Active || window->Hidden)
5934 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
5935 skip_items = true;
5936 window->SkipItems = skip_items;
5937
5938 return !skip_items;
5939 }
5940
End()5941 void ImGui::End()
5942 {
5943 ImGuiContext& g = *GImGui;
5944 ImGuiWindow* window = g.CurrentWindow;
5945
5946 // Error checking: verify that user hasn't called End() too many times!
5947 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
5948 {
5949 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
5950 return;
5951 }
5952 IM_ASSERT(g.CurrentWindowStack.Size > 0);
5953
5954 // Error checking: verify that user doesn't directly call End() on a child window.
5955 if (window->Flags & ImGuiWindowFlags_ChildWindow)
5956 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
5957
5958 // Close anything that is open
5959 if (window->DC.CurrentColumns)
5960 EndColumns();
5961 PopClipRect(); // Inner window clip rectangle
5962
5963 // Stop logging
5964 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5965 LogFinish();
5966
5967 // Pop from window stack
5968 g.CurrentWindowStack.pop_back();
5969 if (window->Flags & ImGuiWindowFlags_Popup)
5970 g.BeginPopupStack.pop_back();
5971 ErrorCheckBeginEndCompareStacksSize(window, false);
5972 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5973 }
5974
BringWindowToFocusFront(ImGuiWindow * window)5975 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5976 {
5977 ImGuiContext& g = *GImGui;
5978 if (g.WindowsFocusOrder.back() == window)
5979 return;
5980 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
5981 if (g.WindowsFocusOrder[i] == window)
5982 {
5983 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5984 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5985 break;
5986 }
5987 }
5988
BringWindowToDisplayFront(ImGuiWindow * window)5989 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5990 {
5991 ImGuiContext& g = *GImGui;
5992 ImGuiWindow* current_front_window = g.Windows.back();
5993 if (current_front_window == window || current_front_window->RootWindow == window)
5994 return;
5995 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
5996 if (g.Windows[i] == window)
5997 {
5998 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5999 g.Windows[g.Windows.Size - 1] = window;
6000 break;
6001 }
6002 }
6003
BringWindowToDisplayBack(ImGuiWindow * window)6004 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
6005 {
6006 ImGuiContext& g = *GImGui;
6007 if (g.Windows[0] == window)
6008 return;
6009 for (int i = 0; i < g.Windows.Size; i++)
6010 if (g.Windows[i] == window)
6011 {
6012 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
6013 g.Windows[0] = window;
6014 break;
6015 }
6016 }
6017
6018 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)6019 void ImGui::FocusWindow(ImGuiWindow* window)
6020 {
6021 ImGuiContext& g = *GImGui;
6022
6023 if (g.NavWindow != window)
6024 {
6025 g.NavWindow = window;
6026 if (window && g.NavDisableMouseHover)
6027 g.NavMousePosDirty = true;
6028 g.NavInitRequest = false;
6029 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
6030 g.NavFocusScopeId = 0;
6031 g.NavIdIsAlive = false;
6032 g.NavLayer = ImGuiNavLayer_Main;
6033 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
6034 }
6035
6036 // Close popups if any
6037 ClosePopupsOverWindow(window, false);
6038
6039 // Passing NULL allow to disable keyboard focus
6040 if (!window)
6041 return;
6042
6043 // Move the root window to the top of the pile
6044 IM_ASSERT(window->RootWindow != NULL);
6045 ImGuiWindow* focus_front_window = window->RootWindow; // NB: In docking branch this is window->RootWindowDockStop
6046 ImGuiWindow* display_front_window = window->RootWindow;
6047
6048 // Steal focus on active widgets
6049 if (focus_front_window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement may be unnecessary? Need further testing before removing it..
6050 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
6051 ClearActiveID();
6052
6053 // Bring to front
6054 BringWindowToFocusFront(focus_front_window);
6055 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
6056 BringWindowToDisplayFront(display_front_window);
6057 }
6058
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)6059 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
6060 {
6061 ImGuiContext& g = *GImGui;
6062
6063 int start_idx = g.WindowsFocusOrder.Size - 1;
6064 if (under_this_window != NULL)
6065 {
6066 int under_this_window_idx = FindWindowFocusIndex(under_this_window);
6067 if (under_this_window_idx != -1)
6068 start_idx = under_this_window_idx - 1;
6069 }
6070 for (int i = start_idx; i >= 0; i--)
6071 {
6072 // 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.
6073 ImGuiWindow* window = g.WindowsFocusOrder[i];
6074 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
6075 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
6076 {
6077 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
6078 FocusWindow(focus_window);
6079 return;
6080 }
6081 }
6082 FocusWindow(NULL);
6083 }
6084
SetNextItemWidth(float item_width)6085 void ImGui::SetNextItemWidth(float item_width)
6086 {
6087 ImGuiContext& g = *GImGui;
6088 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
6089 g.NextItemData.Width = item_width;
6090 }
6091
PushItemWidth(float item_width)6092 void ImGui::PushItemWidth(float item_width)
6093 {
6094 ImGuiContext& g = *GImGui;
6095 ImGuiWindow* window = g.CurrentWindow;
6096 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
6097 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
6098 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6099 }
6100
PushMultiItemsWidths(int components,float w_full)6101 void ImGui::PushMultiItemsWidths(int components, float w_full)
6102 {
6103 ImGuiContext& g = *GImGui;
6104 ImGuiWindow* window = g.CurrentWindow;
6105 const ImGuiStyle& style = g.Style;
6106 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
6107 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
6108 window->DC.ItemWidthStack.push_back(w_item_last);
6109 for (int i = 0; i < components-1; i++)
6110 window->DC.ItemWidthStack.push_back(w_item_one);
6111 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
6112 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6113 }
6114
PopItemWidth()6115 void ImGui::PopItemWidth()
6116 {
6117 ImGuiWindow* window = GetCurrentWindow();
6118 window->DC.ItemWidthStack.pop_back();
6119 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
6120 }
6121
6122 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
6123 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()6124 float ImGui::CalcItemWidth()
6125 {
6126 ImGuiContext& g = *GImGui;
6127 ImGuiWindow* window = g.CurrentWindow;
6128 float w;
6129 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
6130 w = g.NextItemData.Width;
6131 else
6132 w = window->DC.ItemWidth;
6133 if (w < 0.0f)
6134 {
6135 float region_max_x = GetContentRegionMaxAbs().x;
6136 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
6137 }
6138 w = IM_FLOOR(w);
6139 return w;
6140 }
6141
6142 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
6143 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
6144 // Note that only CalcItemWidth() is publicly exposed.
6145 // 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)6146 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
6147 {
6148 ImGuiWindow* window = GImGui->CurrentWindow;
6149
6150 ImVec2 region_max;
6151 if (size.x < 0.0f || size.y < 0.0f)
6152 region_max = GetContentRegionMaxAbs();
6153
6154 if (size.x == 0.0f)
6155 size.x = default_w;
6156 else if (size.x < 0.0f)
6157 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
6158
6159 if (size.y == 0.0f)
6160 size.y = default_h;
6161 else if (size.y < 0.0f)
6162 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
6163
6164 return size;
6165 }
6166
SetCurrentFont(ImFont * font)6167 void ImGui::SetCurrentFont(ImFont* font)
6168 {
6169 ImGuiContext& g = *GImGui;
6170 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6171 IM_ASSERT(font->Scale > 0.0f);
6172 g.Font = font;
6173 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6174 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6175
6176 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6177 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6178 g.DrawListSharedData.Font = g.Font;
6179 g.DrawListSharedData.FontSize = g.FontSize;
6180 }
6181
PushFont(ImFont * font)6182 void ImGui::PushFont(ImFont* font)
6183 {
6184 ImGuiContext& g = *GImGui;
6185 if (!font)
6186 font = GetDefaultFont();
6187 SetCurrentFont(font);
6188 g.FontStack.push_back(font);
6189 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6190 }
6191
PopFont()6192 void ImGui::PopFont()
6193 {
6194 ImGuiContext& g = *GImGui;
6195 g.CurrentWindow->DrawList->PopTextureID();
6196 g.FontStack.pop_back();
6197 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6198 }
6199
PushItemFlag(ImGuiItemFlags option,bool enabled)6200 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6201 {
6202 ImGuiWindow* window = GetCurrentWindow();
6203 if (enabled)
6204 window->DC.ItemFlags |= option;
6205 else
6206 window->DC.ItemFlags &= ~option;
6207 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6208 }
6209
PopItemFlag()6210 void ImGui::PopItemFlag()
6211 {
6212 ImGuiWindow* window = GetCurrentWindow();
6213 window->DC.ItemFlagsStack.pop_back();
6214 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6215 }
6216
6217 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6218 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6219 {
6220 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6221 }
6222
PopAllowKeyboardFocus()6223 void ImGui::PopAllowKeyboardFocus()
6224 {
6225 PopItemFlag();
6226 }
6227
PushButtonRepeat(bool repeat)6228 void ImGui::PushButtonRepeat(bool repeat)
6229 {
6230 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6231 }
6232
PopButtonRepeat()6233 void ImGui::PopButtonRepeat()
6234 {
6235 PopItemFlag();
6236 }
6237
PushTextWrapPos(float wrap_pos_x)6238 void ImGui::PushTextWrapPos(float wrap_pos_x)
6239 {
6240 ImGuiWindow* window = GetCurrentWindow();
6241 window->DC.TextWrapPos = wrap_pos_x;
6242 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6243 }
6244
PopTextWrapPos()6245 void ImGui::PopTextWrapPos()
6246 {
6247 ImGuiWindow* window = GetCurrentWindow();
6248 window->DC.TextWrapPosStack.pop_back();
6249 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6250 }
6251
6252 // 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)6253 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6254 {
6255 ImGuiContext& g = *GImGui;
6256 ImGuiColorMod backup;
6257 backup.Col = idx;
6258 backup.BackupValue = g.Style.Colors[idx];
6259 g.ColorModifiers.push_back(backup);
6260 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6261 }
6262
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6263 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6264 {
6265 ImGuiContext& g = *GImGui;
6266 ImGuiColorMod backup;
6267 backup.Col = idx;
6268 backup.BackupValue = g.Style.Colors[idx];
6269 g.ColorModifiers.push_back(backup);
6270 g.Style.Colors[idx] = col;
6271 }
6272
PopStyleColor(int count)6273 void ImGui::PopStyleColor(int count)
6274 {
6275 ImGuiContext& g = *GImGui;
6276 while (count > 0)
6277 {
6278 ImGuiColorMod& backup = g.ColorModifiers.back();
6279 g.Style.Colors[backup.Col] = backup.BackupValue;
6280 g.ColorModifiers.pop_back();
6281 count--;
6282 }
6283 }
6284
6285 struct ImGuiStyleVarInfo
6286 {
6287 ImGuiDataType Type;
6288 ImU32 Count;
6289 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo6290 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6291 };
6292
6293 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6294 {
6295 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
6296 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
6297 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
6298 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
6299 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
6300 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
6301 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
6302 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
6303 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
6304 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
6305 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
6306 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
6307 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
6308 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
6309 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
6310 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
6311 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
6312 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
6313 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
6314 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
6315 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
6316 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
6317 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
6318 };
6319
GetStyleVarInfo(ImGuiStyleVar idx)6320 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6321 {
6322 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6323 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6324 return &GStyleVarInfo[idx];
6325 }
6326
PushStyleVar(ImGuiStyleVar idx,float val)6327 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6328 {
6329 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6330 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6331 {
6332 ImGuiContext& g = *GImGui;
6333 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6334 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6335 *pvar = val;
6336 return;
6337 }
6338 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
6339 }
6340
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6341 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6342 {
6343 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6344 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6345 {
6346 ImGuiContext& g = *GImGui;
6347 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6348 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6349 *pvar = val;
6350 return;
6351 }
6352 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
6353 }
6354
PopStyleVar(int count)6355 void ImGui::PopStyleVar(int count)
6356 {
6357 ImGuiContext& g = *GImGui;
6358 while (count > 0)
6359 {
6360 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6361 ImGuiStyleMod& backup = g.StyleModifiers.back();
6362 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6363 void* data = info->GetVarPtr(&g.Style);
6364 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
6365 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6366 g.StyleModifiers.pop_back();
6367 count--;
6368 }
6369 }
6370
GetStyleColorName(ImGuiCol idx)6371 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6372 {
6373 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6374 switch (idx)
6375 {
6376 case ImGuiCol_Text: return "Text";
6377 case ImGuiCol_TextDisabled: return "TextDisabled";
6378 case ImGuiCol_WindowBg: return "WindowBg";
6379 case ImGuiCol_ChildBg: return "ChildBg";
6380 case ImGuiCol_PopupBg: return "PopupBg";
6381 case ImGuiCol_Border: return "Border";
6382 case ImGuiCol_BorderShadow: return "BorderShadow";
6383 case ImGuiCol_FrameBg: return "FrameBg";
6384 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6385 case ImGuiCol_FrameBgActive: return "FrameBgActive";
6386 case ImGuiCol_TitleBg: return "TitleBg";
6387 case ImGuiCol_TitleBgActive: return "TitleBgActive";
6388 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6389 case ImGuiCol_MenuBarBg: return "MenuBarBg";
6390 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6391 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6392 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6393 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6394 case ImGuiCol_CheckMark: return "CheckMark";
6395 case ImGuiCol_SliderGrab: return "SliderGrab";
6396 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6397 case ImGuiCol_Button: return "Button";
6398 case ImGuiCol_ButtonHovered: return "ButtonHovered";
6399 case ImGuiCol_ButtonActive: return "ButtonActive";
6400 case ImGuiCol_Header: return "Header";
6401 case ImGuiCol_HeaderHovered: return "HeaderHovered";
6402 case ImGuiCol_HeaderActive: return "HeaderActive";
6403 case ImGuiCol_Separator: return "Separator";
6404 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6405 case ImGuiCol_SeparatorActive: return "SeparatorActive";
6406 case ImGuiCol_ResizeGrip: return "ResizeGrip";
6407 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6408 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6409 case ImGuiCol_Tab: return "Tab";
6410 case ImGuiCol_TabHovered: return "TabHovered";
6411 case ImGuiCol_TabActive: return "TabActive";
6412 case ImGuiCol_TabUnfocused: return "TabUnfocused";
6413 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
6414 case ImGuiCol_PlotLines: return "PlotLines";
6415 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6416 case ImGuiCol_PlotHistogram: return "PlotHistogram";
6417 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6418 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6419 case ImGuiCol_DragDropTarget: return "DragDropTarget";
6420 case ImGuiCol_NavHighlight: return "NavHighlight";
6421 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6422 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
6423 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
6424 }
6425 IM_ASSERT(0);
6426 return "Unknown";
6427 }
6428
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6429 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6430 {
6431 if (window->RootWindow == potential_parent)
6432 return true;
6433 while (window != NULL)
6434 {
6435 if (window == potential_parent)
6436 return true;
6437 window = window->ParentWindow;
6438 }
6439 return false;
6440 }
6441
IsWindowHovered(ImGuiHoveredFlags flags)6442 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6443 {
6444 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6445 ImGuiContext& g = *GImGui;
6446
6447 if (flags & ImGuiHoveredFlags_AnyWindow)
6448 {
6449 if (g.HoveredWindow == NULL)
6450 return false;
6451 }
6452 else
6453 {
6454 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6455 {
6456 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6457 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6458 return false;
6459 break;
6460 case ImGuiHoveredFlags_RootWindow:
6461 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6462 return false;
6463 break;
6464 case ImGuiHoveredFlags_ChildWindows:
6465 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6466 return false;
6467 break;
6468 default:
6469 if (g.HoveredWindow != g.CurrentWindow)
6470 return false;
6471 break;
6472 }
6473 }
6474
6475 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6476 return false;
6477 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6478 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6479 return false;
6480 return true;
6481 }
6482
IsWindowFocused(ImGuiFocusedFlags flags)6483 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6484 {
6485 ImGuiContext& g = *GImGui;
6486
6487 if (flags & ImGuiFocusedFlags_AnyWindow)
6488 return g.NavWindow != NULL;
6489
6490 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6491 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6492 {
6493 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6494 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6495 case ImGuiFocusedFlags_RootWindow:
6496 return g.NavWindow == g.CurrentWindow->RootWindow;
6497 case ImGuiFocusedFlags_ChildWindows:
6498 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6499 default:
6500 return g.NavWindow == g.CurrentWindow;
6501 }
6502 }
6503
6504 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6505 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6506 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6507 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6508 {
6509 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6510 }
6511
GetWindowWidth()6512 float ImGui::GetWindowWidth()
6513 {
6514 ImGuiWindow* window = GImGui->CurrentWindow;
6515 return window->Size.x;
6516 }
6517
GetWindowHeight()6518 float ImGui::GetWindowHeight()
6519 {
6520 ImGuiWindow* window = GImGui->CurrentWindow;
6521 return window->Size.y;
6522 }
6523
GetWindowPos()6524 ImVec2 ImGui::GetWindowPos()
6525 {
6526 ImGuiContext& g = *GImGui;
6527 ImGuiWindow* window = g.CurrentWindow;
6528 return window->Pos;
6529 }
6530
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6531 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6532 {
6533 // Test condition (NB: bit 0 is always true) and clear flags for next time
6534 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6535 return;
6536
6537 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6538 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6539 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6540
6541 // Set
6542 const ImVec2 old_pos = window->Pos;
6543 window->Pos = ImFloor(pos);
6544 ImVec2 offset = window->Pos - old_pos;
6545 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
6546 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6547 window->DC.CursorStartPos += offset;
6548 }
6549
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6550 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6551 {
6552 ImGuiWindow* window = GetCurrentWindowRead();
6553 SetWindowPos(window, pos, cond);
6554 }
6555
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6556 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6557 {
6558 if (ImGuiWindow* window = FindWindowByName(name))
6559 SetWindowPos(window, pos, cond);
6560 }
6561
GetWindowSize()6562 ImVec2 ImGui::GetWindowSize()
6563 {
6564 ImGuiWindow* window = GetCurrentWindowRead();
6565 return window->Size;
6566 }
6567
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6568 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6569 {
6570 // Test condition (NB: bit 0 is always true) and clear flags for next time
6571 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6572 return;
6573
6574 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6575 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6576
6577 // Set
6578 if (size.x > 0.0f)
6579 {
6580 window->AutoFitFramesX = 0;
6581 window->SizeFull.x = IM_FLOOR(size.x);
6582 }
6583 else
6584 {
6585 window->AutoFitFramesX = 2;
6586 window->AutoFitOnlyGrows = false;
6587 }
6588 if (size.y > 0.0f)
6589 {
6590 window->AutoFitFramesY = 0;
6591 window->SizeFull.y = IM_FLOOR(size.y);
6592 }
6593 else
6594 {
6595 window->AutoFitFramesY = 2;
6596 window->AutoFitOnlyGrows = false;
6597 }
6598 }
6599
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6600 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6601 {
6602 SetWindowSize(GImGui->CurrentWindow, size, cond);
6603 }
6604
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6605 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6606 {
6607 if (ImGuiWindow* window = FindWindowByName(name))
6608 SetWindowSize(window, size, cond);
6609 }
6610
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6611 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6612 {
6613 // Test condition (NB: bit 0 is always true) and clear flags for next time
6614 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6615 return;
6616 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6617
6618 // Set
6619 window->Collapsed = collapsed;
6620 }
6621
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6622 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6623 {
6624 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6625 }
6626
IsWindowCollapsed()6627 bool ImGui::IsWindowCollapsed()
6628 {
6629 ImGuiWindow* window = GetCurrentWindowRead();
6630 return window->Collapsed;
6631 }
6632
IsWindowAppearing()6633 bool ImGui::IsWindowAppearing()
6634 {
6635 ImGuiWindow* window = GetCurrentWindowRead();
6636 return window->Appearing;
6637 }
6638
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6639 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6640 {
6641 if (ImGuiWindow* window = FindWindowByName(name))
6642 SetWindowCollapsed(window, collapsed, cond);
6643 }
6644
SetWindowFocus()6645 void ImGui::SetWindowFocus()
6646 {
6647 FocusWindow(GImGui->CurrentWindow);
6648 }
6649
SetWindowFocus(const char * name)6650 void ImGui::SetWindowFocus(const char* name)
6651 {
6652 if (name)
6653 {
6654 if (ImGuiWindow* window = FindWindowByName(name))
6655 FocusWindow(window);
6656 }
6657 else
6658 {
6659 FocusWindow(NULL);
6660 }
6661 }
6662
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6663 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6664 {
6665 ImGuiContext& g = *GImGui;
6666 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6667 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6668 g.NextWindowData.PosVal = pos;
6669 g.NextWindowData.PosPivotVal = pivot;
6670 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6671 }
6672
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6673 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6674 {
6675 ImGuiContext& g = *GImGui;
6676 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6677 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6678 g.NextWindowData.SizeVal = size;
6679 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6680 }
6681
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6682 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6683 {
6684 ImGuiContext& g = *GImGui;
6685 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6686 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6687 g.NextWindowData.SizeCallback = custom_callback;
6688 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6689 }
6690
6691 // Content size = inner scrollable rectangle, padded with WindowPadding.
6692 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6693 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6694 {
6695 ImGuiContext& g = *GImGui;
6696 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6697 g.NextWindowData.ContentSizeVal = size;
6698 }
6699
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6700 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6701 {
6702 ImGuiContext& g = *GImGui;
6703 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6704 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6705 g.NextWindowData.CollapsedVal = collapsed;
6706 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6707 }
6708
SetNextWindowFocus()6709 void ImGui::SetNextWindowFocus()
6710 {
6711 ImGuiContext& g = *GImGui;
6712 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6713 }
6714
SetNextWindowBgAlpha(float alpha)6715 void ImGui::SetNextWindowBgAlpha(float alpha)
6716 {
6717 ImGuiContext& g = *GImGui;
6718 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6719 g.NextWindowData.BgAlphaVal = alpha;
6720 }
6721
6722 // FIXME: This is in window space (not screen space!). We should try to obsolete all those functions.
GetContentRegionMax()6723 ImVec2 ImGui::GetContentRegionMax()
6724 {
6725 ImGuiContext& g = *GImGui;
6726 ImGuiWindow* window = g.CurrentWindow;
6727 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
6728 if (window->DC.CurrentColumns)
6729 mx.x = window->WorkRect.Max.x - window->Pos.x;
6730 return mx;
6731 }
6732
6733 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()6734 ImVec2 ImGui::GetContentRegionMaxAbs()
6735 {
6736 ImGuiContext& g = *GImGui;
6737 ImGuiWindow* window = g.CurrentWindow;
6738 ImVec2 mx = window->ContentRegionRect.Max;
6739 if (window->DC.CurrentColumns)
6740 mx.x = window->WorkRect.Max.x;
6741 return mx;
6742 }
6743
GetContentRegionAvail()6744 ImVec2 ImGui::GetContentRegionAvail()
6745 {
6746 ImGuiWindow* window = GImGui->CurrentWindow;
6747 return GetContentRegionMaxAbs() - window->DC.CursorPos;
6748 }
6749
6750 // In window space (not screen space!)
GetWindowContentRegionMin()6751 ImVec2 ImGui::GetWindowContentRegionMin()
6752 {
6753 ImGuiWindow* window = GImGui->CurrentWindow;
6754 return window->ContentRegionRect.Min - window->Pos;
6755 }
6756
GetWindowContentRegionMax()6757 ImVec2 ImGui::GetWindowContentRegionMax()
6758 {
6759 ImGuiWindow* window = GImGui->CurrentWindow;
6760 return window->ContentRegionRect.Max - window->Pos;
6761 }
6762
GetWindowContentRegionWidth()6763 float ImGui::GetWindowContentRegionWidth()
6764 {
6765 ImGuiWindow* window = GImGui->CurrentWindow;
6766 return window->ContentRegionRect.GetWidth();
6767 }
6768
GetTextLineHeight()6769 float ImGui::GetTextLineHeight()
6770 {
6771 ImGuiContext& g = *GImGui;
6772 return g.FontSize;
6773 }
6774
GetTextLineHeightWithSpacing()6775 float ImGui::GetTextLineHeightWithSpacing()
6776 {
6777 ImGuiContext& g = *GImGui;
6778 return g.FontSize + g.Style.ItemSpacing.y;
6779 }
6780
GetFrameHeight()6781 float ImGui::GetFrameHeight()
6782 {
6783 ImGuiContext& g = *GImGui;
6784 return g.FontSize + g.Style.FramePadding.y * 2.0f;
6785 }
6786
GetFrameHeightWithSpacing()6787 float ImGui::GetFrameHeightWithSpacing()
6788 {
6789 ImGuiContext& g = *GImGui;
6790 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6791 }
6792
GetWindowDrawList()6793 ImDrawList* ImGui::GetWindowDrawList()
6794 {
6795 ImGuiWindow* window = GetCurrentWindow();
6796 return window->DrawList;
6797 }
6798
GetFont()6799 ImFont* ImGui::GetFont()
6800 {
6801 return GImGui->Font;
6802 }
6803
GetFontSize()6804 float ImGui::GetFontSize()
6805 {
6806 return GImGui->FontSize;
6807 }
6808
GetFontTexUvWhitePixel()6809 ImVec2 ImGui::GetFontTexUvWhitePixel()
6810 {
6811 return GImGui->DrawListSharedData.TexUvWhitePixel;
6812 }
6813
SetWindowFontScale(float scale)6814 void ImGui::SetWindowFontScale(float scale)
6815 {
6816 ImGuiContext& g = *GImGui;
6817 ImGuiWindow* window = GetCurrentWindow();
6818 window->FontWindowScale = scale;
6819 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6820 }
6821
6822 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6823 // 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()6824 ImVec2 ImGui::GetCursorPos()
6825 {
6826 ImGuiWindow* window = GetCurrentWindowRead();
6827 return window->DC.CursorPos - window->Pos + window->Scroll;
6828 }
6829
GetCursorPosX()6830 float ImGui::GetCursorPosX()
6831 {
6832 ImGuiWindow* window = GetCurrentWindowRead();
6833 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6834 }
6835
GetCursorPosY()6836 float ImGui::GetCursorPosY()
6837 {
6838 ImGuiWindow* window = GetCurrentWindowRead();
6839 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6840 }
6841
SetCursorPos(const ImVec2 & local_pos)6842 void ImGui::SetCursorPos(const ImVec2& local_pos)
6843 {
6844 ImGuiWindow* window = GetCurrentWindow();
6845 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6846 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6847 }
6848
SetCursorPosX(float x)6849 void ImGui::SetCursorPosX(float x)
6850 {
6851 ImGuiWindow* window = GetCurrentWindow();
6852 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6853 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6854 }
6855
SetCursorPosY(float y)6856 void ImGui::SetCursorPosY(float y)
6857 {
6858 ImGuiWindow* window = GetCurrentWindow();
6859 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6860 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6861 }
6862
GetCursorStartPos()6863 ImVec2 ImGui::GetCursorStartPos()
6864 {
6865 ImGuiWindow* window = GetCurrentWindowRead();
6866 return window->DC.CursorStartPos - window->Pos;
6867 }
6868
GetCursorScreenPos()6869 ImVec2 ImGui::GetCursorScreenPos()
6870 {
6871 ImGuiWindow* window = GetCurrentWindowRead();
6872 return window->DC.CursorPos;
6873 }
6874
SetCursorScreenPos(const ImVec2 & pos)6875 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6876 {
6877 ImGuiWindow* window = GetCurrentWindow();
6878 window->DC.CursorPos = pos;
6879 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6880 }
6881
ActivateItem(ImGuiID id)6882 void ImGui::ActivateItem(ImGuiID id)
6883 {
6884 ImGuiContext& g = *GImGui;
6885 g.NavNextActivateId = id;
6886 }
6887
PushFocusScope(ImGuiID id)6888 void ImGui::PushFocusScope(ImGuiID id)
6889 {
6890 ImGuiContext& g = *GImGui;
6891 ImGuiWindow* window = g.CurrentWindow;
6892 window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent);
6893 window->DC.NavFocusScopeIdCurrent = id;
6894 }
6895
PopFocusScope()6896 void ImGui::PopFocusScope()
6897 {
6898 ImGuiContext& g = *GImGui;
6899 ImGuiWindow* window = g.CurrentWindow;
6900 window->DC.NavFocusScopeIdCurrent = window->IDStack.back();
6901 window->IDStack.pop_back();
6902 }
6903
SetKeyboardFocusHere(int offset)6904 void ImGui::SetKeyboardFocusHere(int offset)
6905 {
6906 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6907 ImGuiContext& g = *GImGui;
6908 ImGuiWindow* window = g.CurrentWindow;
6909 g.FocusRequestNextWindow = window;
6910 g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset;
6911 g.FocusRequestNextCounterTabStop = INT_MAX;
6912 }
6913
SetItemDefaultFocus()6914 void ImGui::SetItemDefaultFocus()
6915 {
6916 ImGuiContext& g = *GImGui;
6917 ImGuiWindow* window = g.CurrentWindow;
6918 if (!window->Appearing)
6919 return;
6920 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6921 {
6922 g.NavInitRequest = false;
6923 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6924 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6925 NavUpdateAnyRequestFlag();
6926 if (!IsItemVisible())
6927 SetScrollHereY();
6928 }
6929 }
6930
SetStateStorage(ImGuiStorage * tree)6931 void ImGui::SetStateStorage(ImGuiStorage* tree)
6932 {
6933 ImGuiWindow* window = GImGui->CurrentWindow;
6934 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6935 }
6936
GetStateStorage()6937 ImGuiStorage* ImGui::GetStateStorage()
6938 {
6939 ImGuiWindow* window = GImGui->CurrentWindow;
6940 return window->DC.StateStorage;
6941 }
6942
PushID(const char * str_id)6943 void ImGui::PushID(const char* str_id)
6944 {
6945 ImGuiWindow* window = GImGui->CurrentWindow;
6946 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6947 }
6948
PushID(const char * str_id_begin,const char * str_id_end)6949 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6950 {
6951 ImGuiWindow* window = GImGui->CurrentWindow;
6952 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6953 }
6954
PushID(const void * ptr_id)6955 void ImGui::PushID(const void* ptr_id)
6956 {
6957 ImGuiWindow* window = GImGui->CurrentWindow;
6958 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6959 }
6960
PushID(int int_id)6961 void ImGui::PushID(int int_id)
6962 {
6963 ImGuiWindow* window = GImGui->CurrentWindow;
6964 window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
6965 }
6966
6967 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6968 void ImGui::PushOverrideID(ImGuiID id)
6969 {
6970 ImGuiWindow* window = GImGui->CurrentWindow;
6971 window->IDStack.push_back(id);
6972 }
6973
PopID()6974 void ImGui::PopID()
6975 {
6976 ImGuiWindow* window = GImGui->CurrentWindow;
6977 window->IDStack.pop_back();
6978 }
6979
GetID(const char * str_id)6980 ImGuiID ImGui::GetID(const char* str_id)
6981 {
6982 ImGuiWindow* window = GImGui->CurrentWindow;
6983 return window->GetID(str_id);
6984 }
6985
GetID(const char * str_id_begin,const char * str_id_end)6986 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6987 {
6988 ImGuiWindow* window = GImGui->CurrentWindow;
6989 return window->GetID(str_id_begin, str_id_end);
6990 }
6991
GetID(const void * ptr_id)6992 ImGuiID ImGui::GetID(const void* ptr_id)
6993 {
6994 ImGuiWindow* window = GImGui->CurrentWindow;
6995 return window->GetID(ptr_id);
6996 }
6997
IsRectVisible(const ImVec2 & size)6998 bool ImGui::IsRectVisible(const ImVec2& size)
6999 {
7000 ImGuiWindow* window = GImGui->CurrentWindow;
7001 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
7002 }
7003
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)7004 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
7005 {
7006 ImGuiWindow* window = GImGui->CurrentWindow;
7007 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
7008 }
7009
7010 // 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.)
BeginGroup()7011 void ImGui::BeginGroup()
7012 {
7013 ImGuiContext& g = *GImGui;
7014 ImGuiWindow* window = GetCurrentWindow();
7015
7016 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
7017 ImGuiGroupData& group_data = window->DC.GroupStack.back();
7018 group_data.BackupCursorPos = window->DC.CursorPos;
7019 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
7020 group_data.BackupIndent = window->DC.Indent;
7021 group_data.BackupGroupOffset = window->DC.GroupOffset;
7022 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
7023 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
7024 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
7025 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
7026 group_data.EmitItem = true;
7027
7028 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
7029 window->DC.Indent = window->DC.GroupOffset;
7030 window->DC.CursorMaxPos = window->DC.CursorPos;
7031 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
7032 if (g.LogEnabled)
7033 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7034 }
7035
EndGroup()7036 void ImGui::EndGroup()
7037 {
7038 ImGuiContext& g = *GImGui;
7039 ImGuiWindow* window = GetCurrentWindow();
7040 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
7041
7042 ImGuiGroupData& group_data = window->DC.GroupStack.back();
7043
7044 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
7045
7046 window->DC.CursorPos = group_data.BackupCursorPos;
7047 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
7048 window->DC.Indent = group_data.BackupIndent;
7049 window->DC.GroupOffset = group_data.BackupGroupOffset;
7050 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
7051 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
7052 if (g.LogEnabled)
7053 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
7054
7055 if (!group_data.EmitItem)
7056 {
7057 window->DC.GroupStack.pop_back();
7058 return;
7059 }
7060
7061 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.
7062 ItemSize(group_bb.GetSize());
7063 ItemAdd(group_bb, 0);
7064
7065 // 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.
7066 // 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.
7067 // Also if you grep for LastItemId you'll notice it is only used in that context.
7068 // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
7069 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
7070 const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
7071 if (group_contains_curr_active_id)
7072 window->DC.LastItemId = g.ActiveId;
7073 else if (group_contains_prev_active_id)
7074 window->DC.LastItemId = g.ActiveIdPreviousFrame;
7075 window->DC.LastItemRect = group_bb;
7076
7077 // Forward Edited flag
7078 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
7079 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
7080
7081 // Forward Deactivated flag
7082 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
7083 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
7084 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
7085
7086 window->DC.GroupStack.pop_back();
7087 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
7088 }
7089
7090 // Gets back to previous line and continue with horizontal layout
7091 // offset_from_start_x == 0 : follow right after previous item
7092 // offset_from_start_x != 0 : align to specified x position (relative to window/group left)
7093 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
7094 // spacing_w >= 0 : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)7095 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
7096 {
7097 ImGuiWindow* window = GetCurrentWindow();
7098 if (window->SkipItems)
7099 return;
7100
7101 ImGuiContext& g = *GImGui;
7102 if (offset_from_start_x != 0.0f)
7103 {
7104 if (spacing_w < 0.0f) spacing_w = 0.0f;
7105 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
7106 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7107 }
7108 else
7109 {
7110 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7111 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7112 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7113 }
7114 window->DC.CurrLineSize = window->DC.PrevLineSize;
7115 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7116 }
7117
Indent(float indent_w)7118 void ImGui::Indent(float indent_w)
7119 {
7120 ImGuiContext& g = *GImGui;
7121 ImGuiWindow* window = GetCurrentWindow();
7122 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7123 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7124 }
7125
Unindent(float indent_w)7126 void ImGui::Unindent(float indent_w)
7127 {
7128 ImGuiContext& g = *GImGui;
7129 ImGuiWindow* window = GetCurrentWindow();
7130 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7131 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7132 }
7133
7134
7135 //-----------------------------------------------------------------------------
7136 // [SECTION] ERROR CHECKING
7137 //-----------------------------------------------------------------------------
7138
ErrorCheckEndFrame()7139 static void ImGui::ErrorCheckEndFrame()
7140 {
7141 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7142 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7143 ImGuiContext& g = *GImGui;
7144 if (g.CurrentWindowStack.Size != 1)
7145 {
7146 if (g.CurrentWindowStack.Size > 1)
7147 {
7148 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7149 while (g.CurrentWindowStack.Size > 1)
7150 End();
7151 }
7152 else
7153 {
7154 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7155 }
7156 }
7157
7158 }
7159
7160 // Save and compare stack sizes on Begin()/End() to detect usage errors
7161 // Begin() calls this with write=true
7162 // End() calls this with write=false
ErrorCheckBeginEndCompareStacksSize(ImGuiWindow * window,bool write)7163 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write)
7164 {
7165 ImGuiContext& g = *GImGui;
7166 short* p = &window->DC.StackSizesBackup[0];
7167
7168 // Window stacks
7169 // 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)
7170 { 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()
7171 { 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()
7172
7173 // Global stacks
7174 // 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.
7175 { 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()
7176 { 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()
7177 { 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()
7178 { 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()
7179 IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
7180 }
7181
7182
7183 //-----------------------------------------------------------------------------
7184 // [SECTION] SCROLLING
7185 //-----------------------------------------------------------------------------
7186
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)7187 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
7188 {
7189 ImGuiContext& g = *GImGui;
7190 ImVec2 scroll = window->Scroll;
7191 if (window->ScrollTarget.x < FLT_MAX)
7192 {
7193 float cr_x = window->ScrollTargetCenterRatio.x;
7194 float target_x = window->ScrollTarget.x;
7195 if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
7196 target_x = 0.0f;
7197 else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
7198 target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
7199 scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
7200 }
7201 if (window->ScrollTarget.y < FLT_MAX)
7202 {
7203 // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding.
7204 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7205 float cr_y = window->ScrollTargetCenterRatio.y;
7206 float target_y = window->ScrollTarget.y;
7207 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
7208 target_y = 0.0f;
7209 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
7210 target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
7211 scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7212 }
7213 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
7214 if (!window->Collapsed && !window->SkipItems)
7215 {
7216 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7217 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7218 }
7219 return scroll;
7220 }
7221
7222 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7223 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7224 {
7225 ImGuiContext& g = *GImGui;
7226 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7227 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7228
7229 ImVec2 delta_scroll;
7230 if (!window_rect.Contains(item_rect))
7231 {
7232 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7233 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f);
7234 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7235 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7236 if (item_rect.Min.y < window_rect.Min.y)
7237 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7238 else if (item_rect.Max.y >= window_rect.Max.y)
7239 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7240
7241 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
7242 delta_scroll = next_scroll - window->Scroll;
7243 }
7244
7245 // Also scroll parent window to keep us into view if necessary
7246 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7247 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7248
7249 return delta_scroll;
7250 }
7251
GetScrollX()7252 float ImGui::GetScrollX()
7253 {
7254 ImGuiWindow* window = GImGui->CurrentWindow;
7255 return window->Scroll.x;
7256 }
7257
GetScrollY()7258 float ImGui::GetScrollY()
7259 {
7260 ImGuiWindow* window = GImGui->CurrentWindow;
7261 return window->Scroll.y;
7262 }
7263
GetScrollMaxX()7264 float ImGui::GetScrollMaxX()
7265 {
7266 ImGuiWindow* window = GImGui->CurrentWindow;
7267 return window->ScrollMax.x;
7268 }
7269
GetScrollMaxY()7270 float ImGui::GetScrollMaxY()
7271 {
7272 ImGuiWindow* window = GImGui->CurrentWindow;
7273 return window->ScrollMax.y;
7274 }
7275
SetScrollX(float scroll_x)7276 void ImGui::SetScrollX(float scroll_x)
7277 {
7278 ImGuiWindow* window = GetCurrentWindow();
7279 window->ScrollTarget.x = scroll_x;
7280 window->ScrollTargetCenterRatio.x = 0.0f;
7281 }
7282
SetScrollY(float scroll_y)7283 void ImGui::SetScrollY(float scroll_y)
7284 {
7285 ImGuiWindow* window = GetCurrentWindow();
7286 window->ScrollTarget.y = scroll_y;
7287 window->ScrollTargetCenterRatio.y = 0.0f;
7288 }
7289
SetScrollX(ImGuiWindow * window,float new_scroll_x)7290 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
7291 {
7292 window->ScrollTarget.x = new_scroll_x;
7293 window->ScrollTargetCenterRatio.x = 0.0f;
7294 }
7295
SetScrollY(ImGuiWindow * window,float new_scroll_y)7296 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
7297 {
7298 window->ScrollTarget.y = new_scroll_y;
7299 window->ScrollTargetCenterRatio.y = 0.0f;
7300 }
7301
7302
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7303 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7304 {
7305 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7306 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7307 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
7308 window->ScrollTargetCenterRatio.x = center_x_ratio;
7309 }
7310
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7311 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7312 {
7313 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7314 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7315 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7316 local_y -= decoration_up_height;
7317 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
7318 window->ScrollTargetCenterRatio.y = center_y_ratio;
7319 }
7320
SetScrollFromPosX(float local_x,float center_x_ratio)7321 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7322 {
7323 ImGuiContext& g = *GImGui;
7324 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7325 }
7326
SetScrollFromPosY(float local_y,float center_y_ratio)7327 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7328 {
7329 ImGuiContext& g = *GImGui;
7330 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7331 }
7332
7333 // 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)7334 void ImGui::SetScrollHereX(float center_x_ratio)
7335 {
7336 ImGuiContext& g = *GImGui;
7337 ImGuiWindow* window = g.CurrentWindow;
7338 float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
7339 float last_item_width = window->DC.LastItemRect.GetWidth();
7340 target_x += (last_item_width * center_x_ratio) + (g.Style.ItemSpacing.x * (center_x_ratio - 0.5f) * 2.0f); // Precisely aim before, in the middle or after the last item.
7341 SetScrollFromPosX(target_x, center_x_ratio);
7342 }
7343
7344 // 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)7345 void ImGui::SetScrollHereY(float center_y_ratio)
7346 {
7347 ImGuiContext& g = *GImGui;
7348 ImGuiWindow* window = g.CurrentWindow;
7349 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7350 target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (g.Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
7351 SetScrollFromPosY(target_y, center_y_ratio);
7352 }
7353
7354 //-----------------------------------------------------------------------------
7355 // [SECTION] TOOLTIPS
7356 //-----------------------------------------------------------------------------
7357
BeginTooltip()7358 void ImGui::BeginTooltip()
7359 {
7360 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None);
7361 }
7362
BeginTooltipEx(ImGuiWindowFlags extra_flags,ImGuiTooltipFlags tooltip_flags)7363 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags)
7364 {
7365 ImGuiContext& g = *GImGui;
7366
7367 if (g.DragDropWithinSourceOrTarget)
7368 {
7369 // 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)
7370 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7371 // 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.
7372 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7373 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7374 SetNextWindowPos(tooltip_pos);
7375 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7376 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7377 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
7378 }
7379
7380 char window_name[16];
7381 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7382 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
7383 if (ImGuiWindow* window = FindWindowByName(window_name))
7384 if (window->Active)
7385 {
7386 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7387 window->Hidden = true;
7388 window->HiddenFramesCanSkipItems = 1;
7389 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7390 }
7391 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
7392 Begin(window_name, NULL, flags | extra_flags);
7393 }
7394
EndTooltip()7395 void ImGui::EndTooltip()
7396 {
7397 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
7398 End();
7399 }
7400
SetTooltipV(const char * fmt,va_list args)7401 void ImGui::SetTooltipV(const char* fmt, va_list args)
7402 {
7403 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip);
7404 TextV(fmt, args);
7405 EndTooltip();
7406 }
7407
SetTooltip(const char * fmt,...)7408 void ImGui::SetTooltip(const char* fmt, ...)
7409 {
7410 va_list args;
7411 va_start(args, fmt);
7412 SetTooltipV(fmt, args);
7413 va_end(args);
7414 }
7415
7416 //-----------------------------------------------------------------------------
7417 // [SECTION] POPUPS
7418 //-----------------------------------------------------------------------------
7419
IsPopupOpen(ImGuiID id)7420 bool ImGui::IsPopupOpen(ImGuiID id)
7421 {
7422 ImGuiContext& g = *GImGui;
7423 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7424 }
7425
IsPopupOpen(const char * str_id)7426 bool ImGui::IsPopupOpen(const char* str_id)
7427 {
7428 ImGuiContext& g = *GImGui;
7429 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
7430 }
7431
GetTopMostPopupModal()7432 ImGuiWindow* ImGui::GetTopMostPopupModal()
7433 {
7434 ImGuiContext& g = *GImGui;
7435 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
7436 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7437 if (popup->Flags & ImGuiWindowFlags_Modal)
7438 return popup;
7439 return NULL;
7440 }
7441
OpenPopup(const char * str_id)7442 void ImGui::OpenPopup(const char* str_id)
7443 {
7444 ImGuiContext& g = *GImGui;
7445 OpenPopupEx(g.CurrentWindow->GetID(str_id));
7446 }
7447
7448 // Mark popup as open (toggle toward open state).
7449 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7450 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7451 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)7452 void ImGui::OpenPopupEx(ImGuiID id)
7453 {
7454 ImGuiContext& g = *GImGui;
7455 ImGuiWindow* parent_window = g.CurrentWindow;
7456 int current_stack_size = g.BeginPopupStack.Size;
7457 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7458 popup_ref.PopupId = id;
7459 popup_ref.Window = NULL;
7460 popup_ref.SourceWindow = g.NavWindow;
7461 popup_ref.OpenFrameCount = g.FrameCount;
7462 popup_ref.OpenParentId = parent_window->IDStack.back();
7463 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7464 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7465
7466 //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
7467 if (g.OpenPopupStack.Size < current_stack_size + 1)
7468 {
7469 g.OpenPopupStack.push_back(popup_ref);
7470 }
7471 else
7472 {
7473 // 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
7474 // 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
7475 // 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.
7476 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7477 {
7478 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7479 }
7480 else
7481 {
7482 // Close child popups if any, then flag popup for open/reopen
7483 g.OpenPopupStack.resize(current_stack_size + 1);
7484 g.OpenPopupStack[current_stack_size] = popup_ref;
7485 }
7486
7487 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7488 // This is equivalent to what ClosePopupToLevel() does.
7489 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7490 // FocusWindow(parent_window);
7491 }
7492 }
7493
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7494 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7495 {
7496 ImGuiContext& g = *GImGui;
7497 if (g.OpenPopupStack.empty())
7498 return;
7499
7500 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7501 // Don't close our own child popup windows.
7502 int popup_count_to_keep = 0;
7503 if (ref_window)
7504 {
7505 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7506 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7507 {
7508 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7509 if (!popup.Window)
7510 continue;
7511 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7512 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7513 continue;
7514
7515 // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
7516 bool popup_or_descendent_is_ref_window = false;
7517 for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
7518 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
7519 if (popup_window->RootWindow == ref_window->RootWindow)
7520 popup_or_descendent_is_ref_window = true;
7521 if (!popup_or_descendent_is_ref_window)
7522 break;
7523 }
7524 }
7525 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
7526 {
7527 //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7528 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7529 }
7530 }
7531
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7532 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7533 {
7534 ImGuiContext& g = *GImGui;
7535 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7536 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7537 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7538 g.OpenPopupStack.resize(remaining);
7539
7540 if (restore_focus_to_window_under_popup)
7541 {
7542 if (focus_window && !focus_window->WasActive && popup_window)
7543 {
7544 // Fallback
7545 FocusTopMostWindowUnderOne(popup_window, NULL);
7546 }
7547 else
7548 {
7549 if (g.NavLayer == 0 && focus_window)
7550 focus_window = NavRestoreLastChildNavWindow(focus_window);
7551 FocusWindow(focus_window);
7552 }
7553 }
7554 }
7555
7556 // Close the popup we have begin-ed into.
CloseCurrentPopup()7557 void ImGui::CloseCurrentPopup()
7558 {
7559 ImGuiContext& g = *GImGui;
7560 int popup_idx = g.BeginPopupStack.Size - 1;
7561 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7562 return;
7563
7564 // Closing a menu closes its top-most parent popup (unless a modal)
7565 while (popup_idx > 0)
7566 {
7567 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7568 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7569 bool close_parent = false;
7570 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7571 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7572 close_parent = true;
7573 if (!close_parent)
7574 break;
7575 popup_idx--;
7576 }
7577 //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7578 ClosePopupToLevel(popup_idx, true);
7579
7580 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7581 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7582 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7583 if (ImGuiWindow* window = g.NavWindow)
7584 window->DC.NavHideHighlightOneFrame = true;
7585 }
7586
BeginPopupEx(ImGuiID id,ImGuiWindowFlags flags)7587 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)
7588 {
7589 ImGuiContext& g = *GImGui;
7590 if (!IsPopupOpen(id))
7591 {
7592 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7593 return false;
7594 }
7595
7596 char name[20];
7597 if (flags & ImGuiWindowFlags_ChildMenu)
7598 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7599 else
7600 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7601
7602 flags |= ImGuiWindowFlags_Popup;
7603 bool is_open = Begin(name, NULL, flags);
7604 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7605 EndPopup();
7606
7607 return is_open;
7608 }
7609
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7610 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7611 {
7612 ImGuiContext& g = *GImGui;
7613 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7614 {
7615 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7616 return false;
7617 }
7618 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7619 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7620 }
7621
7622 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7623 // 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)7624 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7625 {
7626 ImGuiContext& g = *GImGui;
7627 ImGuiWindow* window = g.CurrentWindow;
7628 const ImGuiID id = window->GetID(name);
7629 if (!IsPopupOpen(id))
7630 {
7631 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7632 return false;
7633 }
7634
7635 // Center modal windows by default
7636 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7637 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7638 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
7639
7640 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
7641 const bool is_open = Begin(name, p_open, flags);
7642 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7643 {
7644 EndPopup();
7645 if (is_open)
7646 ClosePopupToLevel(g.BeginPopupStack.Size, true);
7647 return false;
7648 }
7649 return is_open;
7650 }
7651
EndPopup()7652 void ImGui::EndPopup()
7653 {
7654 ImGuiContext& g = *GImGui;
7655 ImGuiWindow* window = g.CurrentWindow;
7656 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
7657 IM_ASSERT(g.BeginPopupStack.Size > 0);
7658
7659 // Make all menus and popups wrap around for now, may need to expose that policy.
7660 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
7661
7662 // Child-popups don't need to be layed out
7663 IM_ASSERT(g.WithinEndChild == false);
7664 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7665 g.WithinEndChild = true;
7666 End();
7667 g.WithinEndChild = false;
7668 }
7669
OpenPopupOnItemClick(const char * str_id,ImGuiMouseButton mouse_button)7670 bool ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiMouseButton mouse_button)
7671 {
7672 ImGuiWindow* window = GImGui->CurrentWindow;
7673 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7674 {
7675 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!
7676 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7677 OpenPopupEx(id);
7678 return true;
7679 }
7680 return false;
7681 }
7682
7683 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7684 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7685 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,ImGuiMouseButton mouse_button)7686 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiMouseButton mouse_button)
7687 {
7688 ImGuiWindow* window = GImGui->CurrentWindow;
7689 if (window->SkipItems)
7690 return false;
7691 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!
7692 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7693 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7694 OpenPopupEx(id);
7695 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7696 }
7697
BeginPopupContextWindow(const char * str_id,ImGuiMouseButton mouse_button,bool also_over_items)7698 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mouse_button, bool also_over_items)
7699 {
7700 if (!str_id)
7701 str_id = "window_context";
7702 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7703 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7704 if (also_over_items || !IsAnyItemHovered())
7705 OpenPopupEx(id);
7706 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7707 }
7708
BeginPopupContextVoid(const char * str_id,ImGuiMouseButton mouse_button)7709 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiMouseButton mouse_button)
7710 {
7711 if (!str_id)
7712 str_id = "void_context";
7713 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7714 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7715 OpenPopupEx(id);
7716 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7717 }
7718
7719 // 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.)
7720 // 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)7721 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7722 {
7723 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7724 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7725 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7726
7727 // Combo Box policy (we want a connecting edge)
7728 if (policy == ImGuiPopupPositionPolicy_ComboBox)
7729 {
7730 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7731 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7732 {
7733 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7734 if (n != -1 && dir == *last_dir) // Already tried this direction?
7735 continue;
7736 ImVec2 pos;
7737 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
7738 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7739 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7740 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7741 if (!r_outer.Contains(ImRect(pos, pos + size)))
7742 continue;
7743 *last_dir = dir;
7744 return pos;
7745 }
7746 }
7747
7748 // Default popup policy
7749 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7750 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7751 {
7752 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7753 if (n != -1 && dir == *last_dir) // Already tried this direction?
7754 continue;
7755 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);
7756 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);
7757 if (avail_w < size.x || avail_h < size.y)
7758 continue;
7759 ImVec2 pos;
7760 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7761 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
7762 *last_dir = dir;
7763 return pos;
7764 }
7765
7766 // Fallback, try to keep within display
7767 *last_dir = ImGuiDir_None;
7768 ImVec2 pos = ref_pos;
7769 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7770 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7771 return pos;
7772 }
7773
GetWindowAllowedExtentRect(ImGuiWindow * window)7774 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
7775 {
7776 IM_UNUSED(window);
7777 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7778 ImRect r_screen = GetViewportRect();
7779 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7780 return r_screen;
7781 }
7782
FindBestWindowPosForPopup(ImGuiWindow * window)7783 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7784 {
7785 ImGuiContext& g = *GImGui;
7786
7787 ImRect r_outer = GetWindowAllowedExtentRect(window);
7788 if (window->Flags & ImGuiWindowFlags_ChildMenu)
7789 {
7790 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7791 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7792 IM_ASSERT(g.CurrentWindow == window);
7793 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7794 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).
7795 ImRect r_avoid;
7796 if (parent_window->DC.MenuBarAppending)
7797 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7798 else
7799 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);
7800 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7801 }
7802 if (window->Flags & ImGuiWindowFlags_Popup)
7803 {
7804 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7805 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7806 }
7807 if (window->Flags & ImGuiWindowFlags_Tooltip)
7808 {
7809 // Position tooltip (always follows mouse)
7810 float sc = g.Style.MouseCursorScale;
7811 ImVec2 ref_pos = NavCalcPreferredRefPos();
7812 ImRect r_avoid;
7813 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7814 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7815 else
7816 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.
7817 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7818 if (window->AutoPosLastDirection == ImGuiDir_None)
7819 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.
7820 return pos;
7821 }
7822 IM_ASSERT(0);
7823 return window->Pos;
7824 }
7825
7826
7827 //-----------------------------------------------------------------------------
7828 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7829 //-----------------------------------------------------------------------------
7830
7831 // FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing,
7832 // and needs some explanation or serious refactoring.
SetNavID(ImGuiID id,int nav_layer,ImGuiID focus_scope_id)7833 void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id)
7834 {
7835 ImGuiContext& g = *GImGui;
7836 IM_ASSERT(g.NavWindow);
7837 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
7838 g.NavId = id;
7839 g.NavFocusScopeId = focus_scope_id;
7840 g.NavWindow->NavLastIds[nav_layer] = id;
7841 }
7842
SetNavIDWithRectRel(ImGuiID id,int nav_layer,ImGuiID focus_scope_id,const ImRect & rect_rel)7843 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
7844 {
7845 ImGuiContext& g = *GImGui;
7846 SetNavID(id, nav_layer, focus_scope_id);
7847 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
7848 g.NavMousePosDirty = true;
7849 g.NavDisableHighlight = false;
7850 g.NavDisableMouseHover = true;
7851 }
7852
SetFocusID(ImGuiID id,ImGuiWindow * window)7853 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
7854 {
7855 ImGuiContext& g = *GImGui;
7856 IM_ASSERT(id != 0);
7857
7858 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid.
7859 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
7860 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
7861 if (g.NavWindow != window)
7862 g.NavInitRequest = false;
7863 g.NavWindow = window;
7864 g.NavId = id;
7865 g.NavLayer = nav_layer;
7866 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
7867 window->NavLastIds[nav_layer] = id;
7868 if (window->DC.LastItemId == id)
7869 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
7870
7871 if (g.ActiveIdSource == ImGuiInputSource_Nav)
7872 g.NavDisableMouseHover = true;
7873 else
7874 g.NavDisableHighlight = true;
7875 }
7876
ImGetDirQuadrantFromDelta(float dx,float dy)7877 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7878 {
7879 if (ImFabs(dx) > ImFabs(dy))
7880 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7881 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7882 }
7883
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7884 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7885 {
7886 if (a1 < b0)
7887 return a1 - b0;
7888 if (b1 < a0)
7889 return a0 - b1;
7890 return 0.0f;
7891 }
7892
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7893 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7894 {
7895 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7896 {
7897 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7898 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7899 }
7900 else
7901 {
7902 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7903 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7904 }
7905 }
7906
7907 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7908 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7909 {
7910 ImGuiContext& g = *GImGui;
7911 ImGuiWindow* window = g.CurrentWindow;
7912 if (g.NavLayer != window->DC.NavLayerCurrent)
7913 return false;
7914
7915 const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
7916 g.NavScoringCount++;
7917
7918 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7919 if (window->ParentWindow == g.NavWindow)
7920 {
7921 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7922 if (!window->ClipRect.Overlaps(cand))
7923 return false;
7924 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7925 }
7926
7927 // 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)
7928 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7929 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7930
7931 // Compute distance between boxes
7932 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7933 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7934 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
7935 if (dby != 0.0f && dbx != 0.0f)
7936 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7937 float dist_box = ImFabs(dbx) + ImFabs(dby);
7938
7939 // 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)
7940 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7941 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7942 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7943
7944 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7945 ImGuiDir quadrant;
7946 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7947 if (dbx != 0.0f || dby != 0.0f)
7948 {
7949 // For non-overlapping boxes, use distance between boxes
7950 dax = dbx;
7951 day = dby;
7952 dist_axial = dist_box;
7953 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7954 }
7955 else if (dcx != 0.0f || dcy != 0.0f)
7956 {
7957 // For overlapping boxes with different centers, use distance between centers
7958 dax = dcx;
7959 day = dcy;
7960 dist_axial = dist_center;
7961 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7962 }
7963 else
7964 {
7965 // 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)
7966 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7967 }
7968
7969 #if IMGUI_DEBUG_NAV_SCORING
7970 char buf[128];
7971 if (IsMouseHoveringRect(cand.Min, cand.Max))
7972 {
7973 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]);
7974 ImDrawList* draw_list = GetForegroundDrawList(window);
7975 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7976 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7977 draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
7978 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7979 }
7980 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7981 {
7982 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7983 if (quadrant == g.NavMoveDir)
7984 {
7985 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7986 ImDrawList* draw_list = GetForegroundDrawList(window);
7987 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7988 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7989 }
7990 }
7991 #endif
7992
7993 // Is it in the quadrant we're interesting in moving to?
7994 bool new_best = false;
7995 if (quadrant == g.NavMoveDir)
7996 {
7997 // Does it beat the current best candidate?
7998 if (dist_box < result->DistBox)
7999 {
8000 result->DistBox = dist_box;
8001 result->DistCenter = dist_center;
8002 return true;
8003 }
8004 if (dist_box == result->DistBox)
8005 {
8006 // Try using distance between center points to break ties
8007 if (dist_center < result->DistCenter)
8008 {
8009 result->DistCenter = dist_center;
8010 new_best = true;
8011 }
8012 else if (dist_center == result->DistCenter)
8013 {
8014 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
8015 // (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),
8016 // 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.
8017 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
8018 new_best = true;
8019 }
8020 }
8021 }
8022
8023 // 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
8024 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
8025 // 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.
8026 // 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.
8027 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
8028 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
8029 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8030 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))
8031 {
8032 result->DistAxial = dist_axial;
8033 new_best = true;
8034 }
8035
8036 return new_best;
8037 }
8038
8039 // 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)8040 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
8041 {
8042 ImGuiContext& g = *GImGui;
8043 //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.
8044 // return;
8045
8046 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
8047 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
8048
8049 // Process Init Request
8050 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
8051 {
8052 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
8053 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
8054 {
8055 g.NavInitResultId = id;
8056 g.NavInitResultRectRel = nav_bb_rel;
8057 }
8058 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
8059 {
8060 g.NavInitRequest = false; // Found a match, clear request
8061 NavUpdateAnyRequestFlag();
8062 }
8063 }
8064
8065 // Process Move Request (scoring for navigation)
8066 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
8067 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
8068 {
8069 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8070 #if IMGUI_DEBUG_NAV_SCORING
8071 // [DEBUG] Score all items in NavWindow at all times
8072 if (!g.NavMoveRequest)
8073 g.NavMoveDir = g.NavMoveDirLast;
8074 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
8075 #else
8076 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
8077 #endif
8078 if (new_best)
8079 {
8080 result->Window = window;
8081 result->ID = id;
8082 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8083 result->RectRel = nav_bb_rel;
8084 }
8085
8086 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
8087 const float VISIBLE_RATIO = 0.70f;
8088 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
8089 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)
8090 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
8091 {
8092 result = &g.NavMoveResultLocalVisibleSet;
8093 result->Window = window;
8094 result->ID = id;
8095 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent;
8096 result->RectRel = nav_bb_rel;
8097 }
8098 }
8099
8100 // Update window-relative bounding box of navigated item
8101 if (g.NavId == id)
8102 {
8103 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
8104 g.NavLayer = window->DC.NavLayerCurrent;
8105 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent;
8106 g.NavIdIsAlive = true;
8107 g.NavIdTabCounter = window->DC.FocusCounterTabStop;
8108 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
8109 }
8110 }
8111
NavMoveRequestButNoResultYet()8112 bool ImGui::NavMoveRequestButNoResultYet()
8113 {
8114 ImGuiContext& g = *GImGui;
8115 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
8116 }
8117
NavMoveRequestCancel()8118 void ImGui::NavMoveRequestCancel()
8119 {
8120 ImGuiContext& g = *GImGui;
8121 g.NavMoveRequest = false;
8122 NavUpdateAnyRequestFlag();
8123 }
8124
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)8125 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
8126 {
8127 ImGuiContext& g = *GImGui;
8128 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
8129 NavMoveRequestCancel();
8130 g.NavMoveDir = move_dir;
8131 g.NavMoveClipDir = clip_dir;
8132 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
8133 g.NavMoveRequestFlags = move_flags;
8134 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
8135 }
8136
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)8137 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
8138 {
8139 ImGuiContext& g = *GImGui;
8140 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
8141 return;
8142 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
8143 ImRect bb_rel = window->NavRectRel[0];
8144
8145 ImGuiDir clip_dir = g.NavMoveDir;
8146 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8147 {
8148 bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
8149 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
8150 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8151 }
8152 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8153 {
8154 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
8155 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
8156 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8157 }
8158 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8159 {
8160 bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
8161 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
8162 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8163 }
8164 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8165 {
8166 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
8167 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
8168 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8169 }
8170 }
8171
8172 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8173 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8174 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8175 {
8176 ImGuiWindow* parent_window = nav_window;
8177 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8178 parent_window = parent_window->ParentWindow;
8179 if (parent_window && parent_window != nav_window)
8180 parent_window->NavLastChildNavWindow = nav_window;
8181 }
8182
8183 // Restore the last focused child.
8184 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8185 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8186 {
8187 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
8188 }
8189
NavRestoreLayer(ImGuiNavLayer layer)8190 static void NavRestoreLayer(ImGuiNavLayer layer)
8191 {
8192 ImGuiContext& g = *GImGui;
8193 g.NavLayer = layer;
8194 if (layer == 0)
8195 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8196 ImGuiWindow* window = g.NavWindow;
8197 if (layer == 0 && window->NavLastIds[0] != 0)
8198 ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]);
8199 else
8200 ImGui::NavInitWindow(window, true);
8201 }
8202
NavUpdateAnyRequestFlag()8203 static inline void ImGui::NavUpdateAnyRequestFlag()
8204 {
8205 ImGuiContext& g = *GImGui;
8206 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8207 if (g.NavAnyRequest)
8208 IM_ASSERT(g.NavWindow != NULL);
8209 }
8210
8211 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8212 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8213 {
8214 ImGuiContext& g = *GImGui;
8215 IM_ASSERT(window == g.NavWindow);
8216 bool init_for_nav = false;
8217 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8218 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8219 init_for_nav = true;
8220 //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8221 if (init_for_nav)
8222 {
8223 SetNavID(0, g.NavLayer, 0);
8224 g.NavInitRequest = true;
8225 g.NavInitRequestFromMove = false;
8226 g.NavInitResultId = 0;
8227 g.NavInitResultRectRel = ImRect();
8228 NavUpdateAnyRequestFlag();
8229 }
8230 else
8231 {
8232 g.NavId = window->NavLastIds[0];
8233 g.NavFocusScopeId = 0;
8234 }
8235 }
8236
NavCalcPreferredRefPos()8237 static ImVec2 ImGui::NavCalcPreferredRefPos()
8238 {
8239 ImGuiContext& g = *GImGui;
8240 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8241 {
8242 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8243 if (IsMousePosValid(&g.IO.MousePos))
8244 return g.IO.MousePos;
8245 return g.LastValidMousePos;
8246 }
8247 else
8248 {
8249 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8250 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8251 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()));
8252 ImRect visible_rect = GetViewportRect();
8253 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.
8254 }
8255 }
8256
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8257 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8258 {
8259 ImGuiContext& g = *GImGui;
8260 if (mode == ImGuiInputReadMode_Down)
8261 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
8262
8263 const float t = g.IO.NavInputsDownDuration[n];
8264 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
8265 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8266 if (t < 0.0f)
8267 return 0.0f;
8268 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
8269 return (t == 0.0f) ? 1.0f : 0.0f;
8270 if (mode == ImGuiInputReadMode_Repeat)
8271 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8272 if (mode == ImGuiInputReadMode_RepeatSlow)
8273 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8274 if (mode == ImGuiInputReadMode_RepeatFast)
8275 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8276 return 0.0f;
8277 }
8278
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8279 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8280 {
8281 ImVec2 delta(0.0f, 0.0f);
8282 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8283 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
8284 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8285 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
8286 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8287 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8288 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8289 delta *= slow_factor;
8290 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8291 delta *= fast_factor;
8292 return delta;
8293 }
8294
NavUpdate()8295 static void ImGui::NavUpdate()
8296 {
8297 ImGuiContext& g = *GImGui;
8298 g.IO.WantSetMousePos = false;
8299 #if 0
8300 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);
8301 #endif
8302
8303 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard)
8304 // (do it before we map Keyboard input!)
8305 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8306 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8307 if (nav_gamepad_active)
8308 if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f)
8309 g.NavInputSource = ImGuiInputSource_NavGamepad;
8310
8311 // Update Keyboard->Nav inputs mapping
8312 if (nav_keyboard_active)
8313 {
8314 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0)
8315 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
8316 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
8317 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
8318 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8319 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8320 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
8321 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8322 if (g.IO.KeyCtrl)
8323 g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8324 if (g.IO.KeyShift)
8325 g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8326 if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu.
8327 g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
8328 #undef NAV_MAP_KEY
8329 }
8330 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
8331 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
8332 g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f;
8333
8334 // Process navigation init request (select first/default focus)
8335 // 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)
8336 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
8337 {
8338 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8339 //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8340 if (g.NavInitRequestFromMove)
8341 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel);
8342 else
8343 SetNavID(g.NavInitResultId, g.NavLayer, 0);
8344 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8345 }
8346 g.NavInitRequest = false;
8347 g.NavInitRequestFromMove = false;
8348 g.NavInitResultId = 0;
8349 g.NavJustMovedToId = 0;
8350
8351 // Process navigation move request
8352 if (g.NavMoveRequest)
8353 NavUpdateMoveResult();
8354
8355 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8356 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8357 {
8358 IM_ASSERT(g.NavMoveRequest);
8359 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8360 g.NavDisableHighlight = false;
8361 g.NavMoveRequestForward = ImGuiNavForward_None;
8362 }
8363
8364 // Apply application mouse position movement, after we had a chance to process move request result.
8365 if (g.NavMousePosDirty && g.NavIdIsAlive)
8366 {
8367 // Set mouse position given our knowledge of the navigated item position from last frame
8368 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8369 {
8370 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8371 {
8372 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
8373 g.IO.WantSetMousePos = true;
8374 }
8375 }
8376 g.NavMousePosDirty = false;
8377 }
8378 g.NavIdIsAlive = false;
8379 g.NavJustTabbedId = 0;
8380 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8381
8382 // 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
8383 if (g.NavWindow)
8384 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8385 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
8386 g.NavWindow->NavLastChildNavWindow = NULL;
8387
8388 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8389 NavUpdateWindowing();
8390
8391 // Set output flags for user application
8392 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8393 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8394
8395 // Process NavCancel input (to close a popup, get back to parent, clear focus)
8396 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8397 {
8398 if (g.ActiveId != 0)
8399 {
8400 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8401 ClearActiveID();
8402 }
8403 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8404 {
8405 // Exit child window
8406 ImGuiWindow* child_window = g.NavWindow;
8407 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8408 IM_ASSERT(child_window->ChildId != 0);
8409 FocusWindow(parent_window);
8410 SetNavID(child_window->ChildId, 0, 0);
8411 // Reassigning with same value, we're being explicit here.
8412 g.NavIdIsAlive = false; // -V1048
8413 if (g.NavDisableMouseHover)
8414 g.NavMousePosDirty = true;
8415 }
8416 else if (g.OpenPopupStack.Size > 0)
8417 {
8418 // Close open popup/menu
8419 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8420 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8421 }
8422 else if (g.NavLayer != 0)
8423 {
8424 // Leave the "menu" layer
8425 NavRestoreLayer(ImGuiNavLayer_Main);
8426 }
8427 else
8428 {
8429 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8430 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8431 g.NavWindow->NavLastIds[0] = 0;
8432 g.NavId = g.NavFocusScopeId = 0;
8433 }
8434 }
8435
8436 // Process manual activation request
8437 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8438 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8439 {
8440 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8441 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8442 if (g.ActiveId == 0 && activate_pressed)
8443 g.NavActivateId = g.NavId;
8444 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8445 g.NavActivateDownId = g.NavId;
8446 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8447 g.NavActivatePressedId = g.NavId;
8448 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8449 g.NavInputId = g.NavId;
8450 }
8451 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8452 g.NavDisableHighlight = true;
8453 if (g.NavActivateId != 0)
8454 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8455 g.NavMoveRequest = false;
8456
8457 // Process programmatic activation request
8458 if (g.NavNextActivateId != 0)
8459 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8460 g.NavNextActivateId = 0;
8461
8462 // Initiate directional inputs request
8463 if (g.NavMoveRequestForward == ImGuiNavForward_None)
8464 {
8465 g.NavMoveDir = ImGuiDir_None;
8466 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8467 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8468 {
8469 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8470 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8471 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8472 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8473 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8474 }
8475 g.NavMoveClipDir = g.NavMoveDir;
8476 }
8477 else
8478 {
8479 // 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)
8480 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8481 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8482 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8483 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8484 }
8485
8486 // Update PageUp/PageDown/Home/End scroll
8487 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8488 float nav_scoring_rect_offset_y = 0.0f;
8489 if (nav_keyboard_active)
8490 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8491
8492 // 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
8493 if (g.NavMoveDir != ImGuiDir_None)
8494 {
8495 g.NavMoveRequest = true;
8496 g.NavMoveDirLast = g.NavMoveDir;
8497 }
8498 if (g.NavMoveRequest && g.NavId == 0)
8499 {
8500 //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8501 g.NavInitRequest = g.NavInitRequestFromMove = true;
8502 // Reassigning with same value, we're being explicit here.
8503 g.NavInitResultId = 0; // -V1048
8504 g.NavDisableHighlight = false;
8505 }
8506 NavUpdateAnyRequestFlag();
8507
8508 // Scrolling
8509 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8510 {
8511 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8512 ImGuiWindow* window = g.NavWindow;
8513 const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * g.IO.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
8514 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8515 {
8516 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8517 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8518 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8519 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8520 }
8521
8522 // *Normal* Manual scroll with NavScrollXXX keys
8523 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8524 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
8525 if (scroll_dir.x != 0.0f && window->ScrollbarX)
8526 {
8527 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8528 g.NavMoveFromClampedRefRect = true;
8529 }
8530 if (scroll_dir.y != 0.0f)
8531 {
8532 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8533 g.NavMoveFromClampedRefRect = true;
8534 }
8535 }
8536
8537 // Reset search results
8538 g.NavMoveResultLocal.Clear();
8539 g.NavMoveResultLocalVisibleSet.Clear();
8540 g.NavMoveResultOther.Clear();
8541
8542 // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items
8543 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
8544 {
8545 ImGuiWindow* window = g.NavWindow;
8546 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
8547 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8548 {
8549 float pad = window->CalcFontSize() * 0.5f;
8550 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
8551 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
8552 g.NavId = g.NavFocusScopeId = 0;
8553 }
8554 g.NavMoveFromClampedRefRect = false;
8555 }
8556
8557 // 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)
8558 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
8559 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8560 g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
8561 g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
8562 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
8563 IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
8564 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8565 g.NavScoringCount = 0;
8566 #if IMGUI_DEBUG_NAV_RECTS
8567 if (g.NavWindow)
8568 {
8569 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8570 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]
8571 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); }
8572 }
8573 #endif
8574 }
8575
8576 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8577 static void ImGui::NavUpdateMoveResult()
8578 {
8579 ImGuiContext& g = *GImGui;
8580 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8581 {
8582 // 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)
8583 if (g.NavId != 0)
8584 {
8585 g.NavDisableHighlight = false;
8586 g.NavDisableMouseHover = true;
8587 }
8588 return;
8589 }
8590
8591 // Select which result to use
8592 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8593
8594 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8595 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8596 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8597 result = &g.NavMoveResultLocalVisibleSet;
8598
8599 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8600 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8601 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8602 result = &g.NavMoveResultOther;
8603 IM_ASSERT(g.NavWindow && result->Window);
8604
8605 // Scroll to keep newly navigated item fully into view.
8606 if (g.NavLayer == 0)
8607 {
8608 ImVec2 delta_scroll;
8609 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8610 {
8611 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8612 delta_scroll.y = result->Window->Scroll.y - scroll_target;
8613 SetScrollY(result->Window, scroll_target);
8614 }
8615 else
8616 {
8617 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8618 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8619 }
8620
8621 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8622 result->RectRel.TranslateX(-delta_scroll.x);
8623 result->RectRel.TranslateY(-delta_scroll.y);
8624 }
8625
8626 ClearActiveID();
8627 g.NavWindow = result->Window;
8628 if (g.NavId != result->ID)
8629 {
8630 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8631 g.NavJustMovedToId = result->ID;
8632 g.NavJustMovedToFocusScopeId = result->FocusScopeId;
8633
8634 }
8635 SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
8636 g.NavMoveFromClampedRefRect = false;
8637 }
8638
8639 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8640 static float ImGui::NavUpdatePageUpPageDown()
8641 {
8642 ImGuiContext& g = *GImGui;
8643 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8644 return 0.0f;
8645 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
8646 return 0.0f;
8647
8648 ImGuiWindow* window = g.NavWindow;
8649 const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8650 const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8651 const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8652 const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8653 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8654 {
8655 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8656 {
8657 // Fallback manual-scroll when window has no navigable item
8658 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8659 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8660 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8661 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8662 else if (home_pressed)
8663 SetScrollY(window, 0.0f);
8664 else if (end_pressed)
8665 SetScrollY(window, window->ScrollMax.y);
8666 }
8667 else
8668 {
8669 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8670 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8671 float nav_scoring_rect_offset_y = 0.0f;
8672 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8673 {
8674 nav_scoring_rect_offset_y = -page_offset_y;
8675 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)
8676 g.NavMoveClipDir = ImGuiDir_Up;
8677 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8678 }
8679 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8680 {
8681 nav_scoring_rect_offset_y = +page_offset_y;
8682 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)
8683 g.NavMoveClipDir = ImGuiDir_Down;
8684 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8685 }
8686 else if (home_pressed)
8687 {
8688 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8689 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8690 // Preserve current horizontal position if we have any.
8691 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8692 if (nav_rect_rel.IsInverted())
8693 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8694 g.NavMoveDir = ImGuiDir_Down;
8695 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8696 }
8697 else if (end_pressed)
8698 {
8699 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8700 if (nav_rect_rel.IsInverted())
8701 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8702 g.NavMoveDir = ImGuiDir_Up;
8703 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8704 }
8705 return nav_scoring_rect_offset_y;
8706 }
8707 }
8708 return 0.0f;
8709 }
8710
FindWindowFocusIndex(ImGuiWindow * window)8711 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
8712 {
8713 ImGuiContext& g = *GImGui;
8714 for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
8715 if (g.WindowsFocusOrder[i] == window)
8716 return i;
8717 return -1;
8718 }
8719
FindWindowNavFocusable(int i_start,int i_stop,int dir)8720 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
8721 {
8722 ImGuiContext& g = *GImGui;
8723 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
8724 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
8725 return g.WindowsFocusOrder[i];
8726 return NULL;
8727 }
8728
NavUpdateWindowingHighlightWindow(int focus_change_dir)8729 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
8730 {
8731 ImGuiContext& g = *GImGui;
8732 IM_ASSERT(g.NavWindowingTarget);
8733 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
8734 return;
8735
8736 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
8737 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
8738 if (!window_target)
8739 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
8740 if (window_target) // Don't reset windowing target if there's a single window in the list
8741 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
8742 g.NavWindowingToggleLayer = false;
8743 }
8744
8745 // Windowing management mode
8746 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
8747 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()8748 static void ImGui::NavUpdateWindowing()
8749 {
8750 ImGuiContext& g = *GImGui;
8751 ImGuiWindow* apply_focus_window = NULL;
8752 bool apply_toggle_layer = false;
8753
8754 ImGuiWindow* modal_window = GetTopMostPopupModal();
8755 if (modal_window != NULL)
8756 {
8757 g.NavWindowingTarget = NULL;
8758 return;
8759 }
8760
8761 // Fade out
8762 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
8763 {
8764 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
8765 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
8766 g.NavWindowingTargetAnim = NULL;
8767 }
8768
8769 // Start CTRL-TAB or Square+L/R window selection
8770 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
8771 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
8772 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
8773 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8774 {
8775 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop
8776 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8777 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8778 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8779 }
8780
8781 // Gamepad update
8782 g.NavWindowingTimer += g.IO.DeltaTime;
8783 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8784 {
8785 // 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
8786 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8787
8788 // Select window to focus
8789 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8790 if (focus_change_dir != 0)
8791 {
8792 NavUpdateWindowingHighlightWindow(focus_change_dir);
8793 g.NavWindowingHighlightAlpha = 1.0f;
8794 }
8795
8796 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
8797 if (!IsNavInputDown(ImGuiNavInput_Menu))
8798 {
8799 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8800 if (g.NavWindowingToggleLayer && g.NavWindow)
8801 apply_toggle_layer = true;
8802 else if (!g.NavWindowingToggleLayer)
8803 apply_focus_window = g.NavWindowingTarget;
8804 g.NavWindowingTarget = NULL;
8805 }
8806 }
8807
8808 // Keyboard: Focus
8809 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8810 {
8811 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8812 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8813 if (IsKeyPressedMap(ImGuiKey_Tab, true))
8814 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8815 if (!g.IO.KeyCtrl)
8816 apply_focus_window = g.NavWindowingTarget;
8817 }
8818
8819 // Keyboard: Press and Release ALT to toggle menu layer
8820 // 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
8821 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
8822 g.NavWindowingToggleLayer = true;
8823 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8824 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8825 apply_toggle_layer = true;
8826
8827 // Move window
8828 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8829 {
8830 ImVec2 move_delta;
8831 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8832 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8833 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8834 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8835 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8836 {
8837 const float NAV_MOVE_SPEED = 800.0f;
8838 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well
8839 SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always);
8840 g.NavDisableMouseHover = true;
8841 MarkIniSettingsDirty(g.NavWindowingTarget);
8842 }
8843 }
8844
8845 // Apply final focus
8846 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8847 {
8848 ClearActiveID();
8849 g.NavDisableHighlight = false;
8850 g.NavDisableMouseHover = true;
8851 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8852 ClosePopupsOverWindow(apply_focus_window, false);
8853 FocusWindow(apply_focus_window);
8854 if (apply_focus_window->NavLastIds[0] == 0)
8855 NavInitWindow(apply_focus_window, false);
8856
8857 // If the window only has a menu layer, select it directly
8858 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8859 g.NavLayer = ImGuiNavLayer_Menu;
8860 }
8861 if (apply_focus_window)
8862 g.NavWindowingTarget = NULL;
8863
8864 // Apply menu/layer toggle
8865 if (apply_toggle_layer && g.NavWindow)
8866 {
8867 // Move to parent menu if necessary
8868 ImGuiWindow* new_nav_window = g.NavWindow;
8869 while (new_nav_window->ParentWindow
8870 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
8871 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8872 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8873 new_nav_window = new_nav_window->ParentWindow;
8874 if (new_nav_window != g.NavWindow)
8875 {
8876 ImGuiWindow* old_nav_window = g.NavWindow;
8877 FocusWindow(new_nav_window);
8878 new_nav_window->NavLastChildNavWindow = old_nav_window;
8879 }
8880 g.NavDisableHighlight = false;
8881 g.NavDisableMouseHover = true;
8882
8883 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
8884 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
8885 NavRestoreLayer(new_nav_layer);
8886 }
8887 }
8888
8889 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8890 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8891 {
8892 if (window->Flags & ImGuiWindowFlags_Popup)
8893 return "(Popup)";
8894 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8895 return "(Main menu bar)";
8896 return "(Untitled)";
8897 }
8898
8899 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()8900 void ImGui::NavUpdateWindowingOverlay()
8901 {
8902 ImGuiContext& g = *GImGui;
8903 IM_ASSERT(g.NavWindowingTarget != NULL);
8904
8905 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8906 return;
8907
8908 if (g.NavWindowingList == NULL)
8909 g.NavWindowingList = FindWindowByName("###NavWindowingList");
8910 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8911 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8912 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8913 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8914 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8915 {
8916 ImGuiWindow* window = g.WindowsFocusOrder[n];
8917 if (!IsWindowNavFocusable(window))
8918 continue;
8919 const char* label = window->Name;
8920 if (label == FindRenderedTextEnd(label))
8921 label = GetFallbackWindowNameForWindowingList(window);
8922 Selectable(label, g.NavWindowingTarget == window);
8923 }
8924 End();
8925 PopStyleVar();
8926 }
8927
8928
8929 //-----------------------------------------------------------------------------
8930 // [SECTION] DRAG AND DROP
8931 //-----------------------------------------------------------------------------
8932
ClearDragDrop()8933 void ImGui::ClearDragDrop()
8934 {
8935 ImGuiContext& g = *GImGui;
8936 g.DragDropActive = false;
8937 g.DragDropPayload.Clear();
8938 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8939 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8940 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8941 g.DragDropAcceptFrameCount = -1;
8942
8943 g.DragDropPayloadBufHeap.clear();
8944 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8945 }
8946
8947 // Call when current ID is active.
8948 // 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)8949 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8950 {
8951 ImGuiContext& g = *GImGui;
8952 ImGuiWindow* window = g.CurrentWindow;
8953
8954 bool source_drag_active = false;
8955 ImGuiID source_id = 0;
8956 ImGuiID source_parent_id = 0;
8957 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
8958 if (!(flags & ImGuiDragDropFlags_SourceExtern))
8959 {
8960 source_id = window->DC.LastItemId;
8961 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8962 return false;
8963 if (g.IO.MouseDown[mouse_button] == false)
8964 return false;
8965
8966 if (source_id == 0)
8967 {
8968 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8969 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8970 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8971 {
8972 IM_ASSERT(0);
8973 return false;
8974 }
8975
8976 // Early out
8977 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8978 return false;
8979
8980 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8981 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8982 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8983 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8984 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8985 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
8986 if (is_hovered && g.IO.MouseClicked[mouse_button])
8987 {
8988 SetActiveID(source_id, window);
8989 FocusWindow(window);
8990 }
8991 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8992 g.ActiveIdAllowOverlap = is_hovered;
8993 }
8994 else
8995 {
8996 g.ActiveIdAllowOverlap = false;
8997 }
8998 if (g.ActiveId != source_id)
8999 return false;
9000 source_parent_id = window->IDStack.back();
9001 source_drag_active = IsMouseDragging(mouse_button);
9002 }
9003 else
9004 {
9005 window = NULL;
9006 source_id = ImHashStr("#SourceExtern");
9007 source_drag_active = true;
9008 }
9009
9010 if (source_drag_active)
9011 {
9012 if (!g.DragDropActive)
9013 {
9014 IM_ASSERT(source_id != 0);
9015 ClearDragDrop();
9016 ImGuiPayload& payload = g.DragDropPayload;
9017 payload.SourceId = source_id;
9018 payload.SourceParentId = source_parent_id;
9019 g.DragDropActive = true;
9020 g.DragDropSourceFlags = flags;
9021 g.DragDropMouseButton = mouse_button;
9022 }
9023 g.DragDropSourceFrameCount = g.FrameCount;
9024 g.DragDropWithinSourceOrTarget = true;
9025
9026 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9027 {
9028 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
9029 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
9030 BeginTooltip();
9031 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
9032 {
9033 ImGuiWindow* tooltip_window = g.CurrentWindow;
9034 tooltip_window->SkipItems = true;
9035 tooltip_window->HiddenFramesCanSkipItems = 1;
9036 }
9037 }
9038
9039 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
9040 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
9041
9042 return true;
9043 }
9044 return false;
9045 }
9046
EndDragDropSource()9047 void ImGui::EndDragDropSource()
9048 {
9049 ImGuiContext& g = *GImGui;
9050 IM_ASSERT(g.DragDropActive);
9051 IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
9052
9053 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
9054 EndTooltip();
9055
9056 // Discard the drag if have not called SetDragDropPayload()
9057 if (g.DragDropPayload.DataFrameCount == -1)
9058 ClearDragDrop();
9059 g.DragDropWithinSourceOrTarget = false;
9060 }
9061
9062 // 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)9063 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
9064 {
9065 ImGuiContext& g = *GImGui;
9066 ImGuiPayload& payload = g.DragDropPayload;
9067 if (cond == 0)
9068 cond = ImGuiCond_Always;
9069
9070 IM_ASSERT(type != NULL);
9071 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
9072 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
9073 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
9074 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
9075
9076 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
9077 {
9078 // Copy payload
9079 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
9080 g.DragDropPayloadBufHeap.resize(0);
9081 if (data_size > sizeof(g.DragDropPayloadBufLocal))
9082 {
9083 // Store in heap
9084 g.DragDropPayloadBufHeap.resize((int)data_size);
9085 payload.Data = g.DragDropPayloadBufHeap.Data;
9086 memcpy(payload.Data, data, data_size);
9087 }
9088 else if (data_size > 0)
9089 {
9090 // Store locally
9091 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
9092 payload.Data = g.DragDropPayloadBufLocal;
9093 memcpy(payload.Data, data, data_size);
9094 }
9095 else
9096 {
9097 payload.Data = NULL;
9098 }
9099 payload.DataSize = (int)data_size;
9100 }
9101 payload.DataFrameCount = g.FrameCount;
9102
9103 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
9104 }
9105
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)9106 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
9107 {
9108 ImGuiContext& g = *GImGui;
9109 if (!g.DragDropActive)
9110 return false;
9111
9112 ImGuiWindow* window = g.CurrentWindow;
9113 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
9114 return false;
9115 IM_ASSERT(id != 0);
9116 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
9117 return false;
9118 if (window->SkipItems)
9119 return false;
9120
9121 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
9122 g.DragDropTargetRect = bb;
9123 g.DragDropTargetId = id;
9124 g.DragDropWithinSourceOrTarget = true;
9125 return true;
9126 }
9127
9128 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
9129 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
9130 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
9131 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()9132 bool ImGui::BeginDragDropTarget()
9133 {
9134 ImGuiContext& g = *GImGui;
9135 if (!g.DragDropActive)
9136 return false;
9137
9138 ImGuiWindow* window = g.CurrentWindow;
9139 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
9140 return false;
9141 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
9142 return false;
9143
9144 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
9145 ImGuiID id = window->DC.LastItemId;
9146 if (id == 0)
9147 id = window->GetIDFromRectangle(display_rect);
9148 if (g.DragDropPayload.SourceId == id)
9149 return false;
9150
9151 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
9152 g.DragDropTargetRect = display_rect;
9153 g.DragDropTargetId = id;
9154 g.DragDropWithinSourceOrTarget = true;
9155 return true;
9156 }
9157
IsDragDropPayloadBeingAccepted()9158 bool ImGui::IsDragDropPayloadBeingAccepted()
9159 {
9160 ImGuiContext& g = *GImGui;
9161 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9162 }
9163
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9164 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9165 {
9166 ImGuiContext& g = *GImGui;
9167 ImGuiWindow* window = g.CurrentWindow;
9168 ImGuiPayload& payload = g.DragDropPayload;
9169 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9170 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
9171 if (type != NULL && !payload.IsDataType(type))
9172 return NULL;
9173
9174 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9175 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9176 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9177 ImRect r = g.DragDropTargetRect;
9178 float r_surface = r.GetWidth() * r.GetHeight();
9179 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
9180 {
9181 g.DragDropAcceptFlags = flags;
9182 g.DragDropAcceptIdCurr = g.DragDropTargetId;
9183 g.DragDropAcceptIdCurrRectSurface = r_surface;
9184 }
9185
9186 // Render default drop visuals
9187 payload.Preview = was_accepted_previously;
9188 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9189 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9190 {
9191 // FIXME-DRAG: Settle on a proper default visuals for drop target.
9192 r.Expand(3.5f);
9193 bool push_clip_rect = !window->ClipRect.Contains(r);
9194 if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
9195 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9196 if (push_clip_rect) window->DrawList->PopClipRect();
9197 }
9198
9199 g.DragDropAcceptFrameCount = g.FrameCount;
9200 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()
9201 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9202 return NULL;
9203
9204 return &payload;
9205 }
9206
GetDragDropPayload()9207 const ImGuiPayload* ImGui::GetDragDropPayload()
9208 {
9209 ImGuiContext& g = *GImGui;
9210 return g.DragDropActive ? &g.DragDropPayload : NULL;
9211 }
9212
9213 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9214 void ImGui::EndDragDropTarget()
9215 {
9216 ImGuiContext& g = *GImGui;
9217 IM_ASSERT(g.DragDropActive);
9218 IM_ASSERT(g.DragDropWithinSourceOrTarget);
9219 g.DragDropWithinSourceOrTarget = false;
9220 }
9221
9222
9223 //-----------------------------------------------------------------------------
9224 // [SECTION] LOGGING/CAPTURING
9225 //-----------------------------------------------------------------------------
9226 // All text output from the interface can be captured into tty/file/clipboard.
9227 // By default, tree nodes are automatically opened during logging.
9228 //-----------------------------------------------------------------------------
9229
9230 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9231 void ImGui::LogText(const char* fmt, ...)
9232 {
9233 ImGuiContext& g = *GImGui;
9234 if (!g.LogEnabled)
9235 return;
9236
9237 va_list args;
9238 va_start(args, fmt);
9239 if (g.LogFile)
9240 {
9241 g.LogBuffer.Buf.resize(0);
9242 g.LogBuffer.appendfv(fmt, args);
9243 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9244 }
9245 else
9246 {
9247 g.LogBuffer.appendfv(fmt, args);
9248 }
9249 va_end(args);
9250 }
9251
9252 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9253 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9254 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9255 {
9256 ImGuiContext& g = *GImGui;
9257 ImGuiWindow* window = g.CurrentWindow;
9258
9259 if (!text_end)
9260 text_end = FindRenderedTextEnd(text, text_end);
9261
9262 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9263 if (ref_pos)
9264 g.LogLinePosY = ref_pos->y;
9265 if (log_new_line)
9266 g.LogLineFirstItem = true;
9267
9268 const char* text_remaining = text;
9269 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
9270 g.LogDepthRef = window->DC.TreeDepth;
9271 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9272 for (;;)
9273 {
9274 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9275 // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9276 const char* line_start = text_remaining;
9277 const char* line_end = ImStreolRange(line_start, text_end);
9278 const bool is_first_line = (line_start == text);
9279 const bool is_last_line = (line_end == text_end);
9280 if (!is_last_line || (line_start != line_end))
9281 {
9282 const int char_count = (int)(line_end - line_start);
9283 if (log_new_line || !is_first_line)
9284 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9285 else if (g.LogLineFirstItem)
9286 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9287 else
9288 LogText(" %.*s", char_count, line_start);
9289 g.LogLineFirstItem = false;
9290 }
9291 else if (log_new_line)
9292 {
9293 // An empty "" string at a different Y position should output a carriage return.
9294 LogText(IM_NEWLINE);
9295 break;
9296 }
9297
9298 if (is_last_line)
9299 break;
9300 text_remaining = line_end + 1;
9301 }
9302 }
9303
9304 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9305 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9306 {
9307 ImGuiContext& g = *GImGui;
9308 ImGuiWindow* window = g.CurrentWindow;
9309 IM_ASSERT(g.LogEnabled == false);
9310 IM_ASSERT(g.LogFile == NULL);
9311 IM_ASSERT(g.LogBuffer.empty());
9312 g.LogEnabled = true;
9313 g.LogType = type;
9314 g.LogDepthRef = window->DC.TreeDepth;
9315 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9316 g.LogLinePosY = FLT_MAX;
9317 g.LogLineFirstItem = true;
9318 }
9319
LogToTTY(int auto_open_depth)9320 void ImGui::LogToTTY(int auto_open_depth)
9321 {
9322 ImGuiContext& g = *GImGui;
9323 if (g.LogEnabled)
9324 return;
9325 IM_UNUSED(auto_open_depth);
9326 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9327 LogBegin(ImGuiLogType_TTY, auto_open_depth);
9328 g.LogFile = stdout;
9329 #endif
9330 }
9331
9332 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9333 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9334 {
9335 ImGuiContext& g = *GImGui;
9336 if (g.LogEnabled)
9337 return;
9338
9339 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9340 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9341 // By opening the file in binary mode "ab" we have consistent output everywhere.
9342 if (!filename)
9343 filename = g.IO.LogFilename;
9344 if (!filename || !filename[0])
9345 return;
9346 ImFileHandle f = ImFileOpen(filename, "ab");
9347 if (!f)
9348 {
9349 IM_ASSERT(0);
9350 return;
9351 }
9352
9353 LogBegin(ImGuiLogType_File, auto_open_depth);
9354 g.LogFile = f;
9355 }
9356
9357 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9358 void ImGui::LogToClipboard(int auto_open_depth)
9359 {
9360 ImGuiContext& g = *GImGui;
9361 if (g.LogEnabled)
9362 return;
9363 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9364 }
9365
LogToBuffer(int auto_open_depth)9366 void ImGui::LogToBuffer(int auto_open_depth)
9367 {
9368 ImGuiContext& g = *GImGui;
9369 if (g.LogEnabled)
9370 return;
9371 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9372 }
9373
LogFinish()9374 void ImGui::LogFinish()
9375 {
9376 ImGuiContext& g = *GImGui;
9377 if (!g.LogEnabled)
9378 return;
9379
9380 LogText(IM_NEWLINE);
9381 switch (g.LogType)
9382 {
9383 case ImGuiLogType_TTY:
9384 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9385 fflush(g.LogFile);
9386 #endif
9387 break;
9388 case ImGuiLogType_File:
9389 ImFileClose(g.LogFile);
9390 break;
9391 case ImGuiLogType_Buffer:
9392 break;
9393 case ImGuiLogType_Clipboard:
9394 if (!g.LogBuffer.empty())
9395 SetClipboardText(g.LogBuffer.begin());
9396 break;
9397 case ImGuiLogType_None:
9398 IM_ASSERT(0);
9399 break;
9400 }
9401
9402 g.LogEnabled = false;
9403 g.LogType = ImGuiLogType_None;
9404 g.LogFile = NULL;
9405 g.LogBuffer.clear();
9406 }
9407
9408 // Helper to display logging buttons
9409 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9410 void ImGui::LogButtons()
9411 {
9412 ImGuiContext& g = *GImGui;
9413
9414 PushID("LogButtons");
9415 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9416 const bool log_to_tty = Button("Log To TTY"); SameLine();
9417 #else
9418 const bool log_to_tty = false;
9419 #endif
9420 const bool log_to_file = Button("Log To File"); SameLine();
9421 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9422 PushAllowKeyboardFocus(false);
9423 SetNextItemWidth(80.0f);
9424 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9425 PopAllowKeyboardFocus();
9426 PopID();
9427
9428 // Start logging at the end of the function so that the buttons don't appear in the log
9429 if (log_to_tty)
9430 LogToTTY();
9431 if (log_to_file)
9432 LogToFile();
9433 if (log_to_clipboard)
9434 LogToClipboard();
9435 }
9436
9437 //-----------------------------------------------------------------------------
9438 // [SECTION] SETTINGS
9439 //-----------------------------------------------------------------------------
9440
MarkIniSettingsDirty()9441 void ImGui::MarkIniSettingsDirty()
9442 {
9443 ImGuiContext& g = *GImGui;
9444 if (g.SettingsDirtyTimer <= 0.0f)
9445 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9446 }
9447
MarkIniSettingsDirty(ImGuiWindow * window)9448 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9449 {
9450 ImGuiContext& g = *GImGui;
9451 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9452 if (g.SettingsDirtyTimer <= 0.0f)
9453 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9454 }
9455
CreateNewWindowSettings(const char * name)9456 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9457 {
9458 ImGuiContext& g = *GImGui;
9459
9460 #if !IMGUI_DEBUG_INI_SETTINGS
9461 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9462 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9463 if (const char* p = strstr(name, "###"))
9464 name = p;
9465 #endif
9466 const size_t name_len = strlen(name);
9467
9468 // Allocate chunk
9469 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
9470 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
9471 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
9472 settings->ID = ImHashStr(name, name_len);
9473 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
9474
9475 return settings;
9476 }
9477
FindWindowSettings(ImGuiID id)9478 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9479 {
9480 ImGuiContext& g = *GImGui;
9481 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9482 if (settings->ID == id)
9483 return settings;
9484 return NULL;
9485 }
9486
FindOrCreateWindowSettings(const char * name)9487 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9488 {
9489 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9490 return settings;
9491 return CreateNewWindowSettings(name);
9492 }
9493
LoadIniSettingsFromDisk(const char * ini_filename)9494 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9495 {
9496 size_t file_data_size = 0;
9497 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9498 if (!file_data)
9499 return;
9500 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9501 IM_FREE(file_data);
9502 }
9503
FindSettingsHandler(const char * type_name)9504 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9505 {
9506 ImGuiContext& g = *GImGui;
9507 const ImGuiID type_hash = ImHashStr(type_name);
9508 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9509 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9510 return &g.SettingsHandlers[handler_n];
9511 return NULL;
9512 }
9513
9514 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9515 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9516 {
9517 ImGuiContext& g = *GImGui;
9518 IM_ASSERT(g.Initialized);
9519 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9520
9521 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9522 // 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..
9523 if (ini_size == 0)
9524 ini_size = strlen(ini_data);
9525 char* buf = (char*)IM_ALLOC(ini_size + 1);
9526 char* buf_end = buf + ini_size;
9527 memcpy(buf, ini_data, ini_size);
9528 buf[ini_size] = 0;
9529
9530 void* entry_data = NULL;
9531 ImGuiSettingsHandler* entry_handler = NULL;
9532
9533 char* line_end = NULL;
9534 for (char* line = buf; line < buf_end; line = line_end + 1)
9535 {
9536 // Skip new lines markers, then find end of the line
9537 while (*line == '\n' || *line == '\r')
9538 line++;
9539 line_end = line;
9540 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9541 line_end++;
9542 line_end[0] = 0;
9543 if (line[0] == ';')
9544 continue;
9545 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9546 {
9547 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9548 line_end[-1] = 0;
9549 const char* name_end = line_end - 1;
9550 const char* type_start = line + 1;
9551 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
9552 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9553 if (!type_end || !name_start)
9554 continue;
9555 *type_end = 0; // Overwrite first ']'
9556 name_start++; // Skip second '['
9557 entry_handler = FindSettingsHandler(type_start);
9558 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9559 }
9560 else if (entry_handler != NULL && entry_data != NULL)
9561 {
9562 // Let type handler parse the line
9563 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9564 }
9565 }
9566 IM_FREE(buf);
9567 g.SettingsLoaded = true;
9568 }
9569
SaveIniSettingsToDisk(const char * ini_filename)9570 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9571 {
9572 ImGuiContext& g = *GImGui;
9573 g.SettingsDirtyTimer = 0.0f;
9574 if (!ini_filename)
9575 return;
9576
9577 size_t ini_data_size = 0;
9578 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9579 ImFileHandle f = ImFileOpen(ini_filename, "wt");
9580 if (!f)
9581 return;
9582 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
9583 ImFileClose(f);
9584 }
9585
9586 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9587 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9588 {
9589 ImGuiContext& g = *GImGui;
9590 g.SettingsDirtyTimer = 0.0f;
9591 g.SettingsIniData.Buf.resize(0);
9592 g.SettingsIniData.Buf.push_back(0);
9593 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9594 {
9595 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9596 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9597 }
9598 if (out_size)
9599 *out_size = (size_t)g.SettingsIniData.size();
9600 return g.SettingsIniData.c_str();
9601 }
9602
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9603 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9604 {
9605 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
9606 if (!settings)
9607 settings = ImGui::CreateNewWindowSettings(name);
9608 return (void*)settings;
9609 }
9610
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9611 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9612 {
9613 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9614 int x, y;
9615 int i;
9616 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) settings->Pos = ImVec2ih((short)x, (short)y);
9617 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) settings->Size = ImVec2ih((short)x, (short)y);
9618 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
9619 }
9620
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9621 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9622 {
9623 // Gather data from windows that were active during this session
9624 // (if a window wasn't opened in this session we preserve its settings)
9625 ImGuiContext& g = *ctx;
9626 for (int i = 0; i != g.Windows.Size; i++)
9627 {
9628 ImGuiWindow* window = g.Windows[i];
9629 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9630 continue;
9631
9632 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
9633 if (!settings)
9634 {
9635 settings = ImGui::CreateNewWindowSettings(window->Name);
9636 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
9637 }
9638 IM_ASSERT(settings->ID == window->ID);
9639 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
9640 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
9641 settings->Collapsed = window->Collapsed;
9642 }
9643
9644 // Write to text buffer
9645 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
9646 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9647 {
9648 const char* settings_name = settings->GetName();
9649 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
9650 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
9651 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
9652 buf->appendf("Collapsed=%d\n", settings->Collapsed);
9653 buf->append("\n");
9654 }
9655 }
9656
9657
9658 //-----------------------------------------------------------------------------
9659 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
9660 //-----------------------------------------------------------------------------
9661
9662 // (this section is filled in the 'docking' branch)
9663
9664
9665 //-----------------------------------------------------------------------------
9666 // [SECTION] DOCKING
9667 //-----------------------------------------------------------------------------
9668
9669 // (this section is filled in the 'docking' branch)
9670
9671
9672 //-----------------------------------------------------------------------------
9673 // [SECTION] PLATFORM DEPENDENT HELPERS
9674 //-----------------------------------------------------------------------------
9675
9676 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9677 #ifndef WIN32_LEAN_AND_MEAN
9678 #define WIN32_LEAN_AND_MEAN
9679 #endif
9680 #ifndef __MINGW32__
9681 #include <Windows.h>
9682 #else
9683 #include <windows.h>
9684 #endif
9685 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
9686 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
9687 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
9688 #endif
9689 #elif defined(__APPLE__)
9690 #include <TargetConditionals.h>
9691 #endif
9692
9693 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9694
9695 #ifdef _MSC_VER
9696 #pragma comment(lib, "user32")
9697 #endif
9698
9699 // Win32 clipboard implementation
GetClipboardTextFn_DefaultImpl(void *)9700 static const char* GetClipboardTextFn_DefaultImpl(void*)
9701 {
9702 static ImVector<char> buf_local;
9703 buf_local.clear();
9704 if (!::OpenClipboard(NULL))
9705 return NULL;
9706 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9707 if (wbuf_handle == NULL)
9708 {
9709 ::CloseClipboard();
9710 return NULL;
9711 }
9712 if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9713 {
9714 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9715 buf_local.resize(buf_len);
9716 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9717 }
9718 ::GlobalUnlock(wbuf_handle);
9719 ::CloseClipboard();
9720 return buf_local.Data;
9721 }
9722
SetClipboardTextFn_DefaultImpl(void *,const char * text)9723 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9724 {
9725 if (!::OpenClipboard(NULL))
9726 return;
9727 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9728 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9729 if (wbuf_handle == NULL)
9730 {
9731 ::CloseClipboard();
9732 return;
9733 }
9734 ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9735 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9736 ::GlobalUnlock(wbuf_handle);
9737 ::EmptyClipboard();
9738 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9739 ::GlobalFree(wbuf_handle);
9740 ::CloseClipboard();
9741 }
9742
9743 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
9744
9745 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
9746 static PasteboardRef main_clipboard = 0;
9747
9748 // OSX clipboard implementation
9749 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)9750 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9751 {
9752 if (!main_clipboard)
9753 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9754 PasteboardClear(main_clipboard);
9755 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
9756 if (cf_data)
9757 {
9758 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
9759 CFRelease(cf_data);
9760 }
9761 }
9762
GetClipboardTextFn_DefaultImpl(void *)9763 static const char* GetClipboardTextFn_DefaultImpl(void*)
9764 {
9765 if (!main_clipboard)
9766 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9767 PasteboardSynchronize(main_clipboard);
9768
9769 ItemCount item_count = 0;
9770 PasteboardGetItemCount(main_clipboard, &item_count);
9771 for (ItemCount i = 0; i < item_count; i++)
9772 {
9773 PasteboardItemID item_id = 0;
9774 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
9775 CFArrayRef flavor_type_array = 0;
9776 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
9777 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
9778 {
9779 CFDataRef cf_data;
9780 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
9781 {
9782 static ImVector<char> clipboard_text;
9783 int length = (int)CFDataGetLength(cf_data);
9784 clipboard_text.resize(length + 1);
9785 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
9786 clipboard_text[length] = 0;
9787 CFRelease(cf_data);
9788 return clipboard_text.Data;
9789 }
9790 }
9791 }
9792 return NULL;
9793 }
9794
9795 #else
9796
9797 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)9798 static const char* GetClipboardTextFn_DefaultImpl(void*)
9799 {
9800 ImGuiContext& g = *GImGui;
9801 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9802 }
9803
SetClipboardTextFn_DefaultImpl(void *,const char * text)9804 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9805 {
9806 ImGuiContext& g = *GImGui;
9807 g.PrivateClipboard.clear();
9808 const char* text_end = text + strlen(text);
9809 g.PrivateClipboard.resize((int)(text_end - text) + 1);
9810 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9811 g.PrivateClipboard[(int)(text_end - text)] = 0;
9812 }
9813
9814 #endif
9815
9816 // Win32 API IME support (for Asian languages, etc.)
9817 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9818
9819 #include <imm.h>
9820 #ifdef _MSC_VER
9821 #pragma comment(lib, "imm32")
9822 #endif
9823
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9824 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9825 {
9826 // Notify OS Input Method Editor of text input position
9827 ImGuiIO& io = ImGui::GetIO();
9828 if (HWND hwnd = (HWND)io.ImeWindowHandle)
9829 if (HIMC himc = ::ImmGetContext(hwnd))
9830 {
9831 COMPOSITIONFORM cf;
9832 cf.ptCurrentPos.x = x;
9833 cf.ptCurrentPos.y = y;
9834 cf.dwStyle = CFS_FORCE_POSITION;
9835 ::ImmSetCompositionWindow(himc, &cf);
9836 ::ImmReleaseContext(hwnd, himc);
9837 }
9838 }
9839
9840 #else
9841
ImeSetInputScreenPosFn_DefaultImpl(int,int)9842 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9843
9844 #endif
9845
9846 //-----------------------------------------------------------------------------
9847 // [SECTION] METRICS/DEBUG WINDOW
9848 //-----------------------------------------------------------------------------
9849
9850 #ifndef IMGUI_DISABLE_METRICS_WINDOW
9851 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)9852 static void MetricsHelpMarker(const char* desc)
9853 {
9854 ImGui::TextDisabled("(?)");
9855 if (ImGui::IsItemHovered())
9856 {
9857 ImGui::BeginTooltip();
9858 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
9859 ImGui::TextUnformatted(desc);
9860 ImGui::PopTextWrapPos();
9861 ImGui::EndTooltip();
9862 }
9863 }
9864
ShowMetricsWindow(bool * p_open)9865 void ImGui::ShowMetricsWindow(bool* p_open)
9866 {
9867 if (!ImGui::Begin("Dear ImGui Metrics", p_open))
9868 {
9869 ImGui::End();
9870 return;
9871 }
9872
9873 // Debugging enums
9874 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
9875 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
9876 enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersDesired, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type
9877 const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersDesired", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" };
9878
9879 // State
9880 static bool show_windows_rects = false;
9881 static int show_windows_rect_type = WRT_WorkRect;
9882 static bool show_windows_begin_order = false;
9883 static bool show_tables_rects = false;
9884 static int show_tables_rect_type = TRT_WorkRect;
9885 static bool show_drawcmd_details = true;
9886
9887 // Basic info
9888 ImGuiContext& g = *GImGui;
9889 ImGuiIO& io = ImGui::GetIO();
9890 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9891 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9892 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9893 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9894 ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
9895 ImGui::Separator();
9896
9897 // Helper functions to display common structures:
9898 // - NodeDrawList()
9899 // - NodeColumns()
9900 // - NodeWindow()
9901 // - NodeWindows()
9902 // - NodeTabBar()
9903 // - NodeStorage()
9904 struct Funcs
9905 {
9906 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
9907 {
9908 if (rect_type == WRT_OuterRect) { return window->Rect(); }
9909 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
9910 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
9911 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
9912 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
9913 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
9914 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
9915 IM_ASSERT(0);
9916 return ImRect();
9917 }
9918
9919 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9920 {
9921 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);
9922 if (draw_list == ImGui::GetWindowDrawList())
9923 {
9924 ImGui::SameLine();
9925 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)
9926 if (node_open) ImGui::TreePop();
9927 return;
9928 }
9929
9930 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
9931 if (window && IsItemHovered())
9932 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9933 if (!node_open)
9934 return;
9935
9936 if (window && !window->WasActive)
9937 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
9938
9939 unsigned int elem_offset = 0;
9940 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9941 {
9942 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9943 continue;
9944 if (pcmd->UserCallback)
9945 {
9946 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9947 continue;
9948 }
9949
9950 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9951 char buf[300];
9952 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd: %4d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
9953 pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId,
9954 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9955 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
9956 if (show_drawcmd_details && fg_draw_list && ImGui::IsItemHovered())
9957 {
9958 ImRect clip_rect = pcmd->ClipRect;
9959 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
9960 for (unsigned int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9961 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9962 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255,0,255,255));
9963 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(255,255,0,255));
9964 }
9965 if (!pcmd_node_open)
9966 continue;
9967
9968 // Calculate approximate coverage area (touched pixel count)
9969 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
9970 float total_area = 0.0f;
9971 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9972 {
9973 ImVec2 triangle[3];
9974 for (int n = 0; n < 3; n++)
9975 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9976 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
9977 }
9978
9979 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
9980 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
9981 ImGui::Selectable(buf);
9982 if (fg_draw_list && ImGui::IsItemHovered() && show_drawcmd_details)
9983 {
9984 // Draw wire-frame version of everything
9985 ImDrawListFlags backup_flags = fg_draw_list->Flags;
9986 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
9987 ImRect clip_rect = pcmd->ClipRect;
9988 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255));
9989 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9990 {
9991 ImVec2 triangle[3];
9992 for (int n = 0; n < 3; n++)
9993 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9994 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
9995 }
9996 fg_draw_list->Flags = backup_flags;
9997 }
9998
9999 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
10000 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
10001 while (clipper.Step())
10002 for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
10003 {
10004 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
10005 ImVec2 triangle[3];
10006 for (int n = 0; n < 3; n++, idx_i++)
10007 {
10008 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
10009 triangle[n] = v.pos;
10010 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
10011 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
10012 }
10013
10014 ImGui::Selectable(buf, false);
10015 if (fg_draw_list && ImGui::IsItemHovered())
10016 {
10017 ImDrawListFlags backup_flags = fg_draw_list->Flags;
10018 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
10019 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f);
10020 fg_draw_list->Flags = backup_flags;
10021 }
10022 }
10023 ImGui::TreePop();
10024 }
10025 ImGui::TreePop();
10026 }
10027
10028 static void NodeColumns(const ImGuiColumns* columns)
10029 {
10030 if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
10031 return;
10032 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
10033 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
10034 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
10035 ImGui::TreePop();
10036 }
10037
10038 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
10039 {
10040 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
10041 return;
10042 for (int i = 0; i < windows.Size; i++)
10043 {
10044 ImGui::PushID(windows[i]);
10045 Funcs::NodeWindow(windows[i], "Window");
10046 ImGui::PopID();
10047 }
10048 ImGui::TreePop();
10049 }
10050
10051 static void NodeWindow(ImGuiWindow* window, const char* label)
10052 {
10053 if (window == NULL)
10054 {
10055 ImGui::BulletText("%s: NULL", label);
10056 return;
10057 }
10058 bool open = ImGui::TreeNode(label, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window);
10059 if (ImGui::IsItemHovered() && window->WasActive)
10060 ImGui::GetForegroundDrawList()->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
10061 if (!open)
10062 return;
10063 ImGuiWindowFlags flags = window->Flags;
10064 NodeDrawList(window, window->DrawList, "DrawList");
10065 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);
10066 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
10067 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
10068 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
10069 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
10070 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" : "");
10071 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
10072 ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
10073 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
10074 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
10075 if (!window->NavRectRel[0].IsInverted())
10076 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);
10077 else
10078 ImGui::BulletText("NavRectRel[0]: <None>");
10079 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
10080 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
10081 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
10082 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
10083 {
10084 for (int n = 0; n < window->ColumnsStorage.Size; n++)
10085 NodeColumns(&window->ColumnsStorage[n]);
10086 ImGui::TreePop();
10087 }
10088 NodeStorage(&window->StateStorage, "Storage");
10089 ImGui::TreePop();
10090 }
10091
10092 static void NodeTabBar(ImGuiTabBar* tab_bar)
10093 {
10094 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
10095 char buf[256];
10096 char* p = buf;
10097 const char* buf_end = buf + IM_ARRAYSIZE(buf);
10098 p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
10099 if (ImGui::TreeNode(tab_bar, "%s", buf))
10100 {
10101 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
10102 {
10103 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
10104 ImGui::PushID(tab);
10105 if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
10106 if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
10107 ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "");
10108 ImGui::PopID();
10109 }
10110 ImGui::TreePop();
10111 }
10112 }
10113
10114 static void NodeStorage(ImGuiStorage* storage, const char* label)
10115 {
10116 if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
10117 return;
10118 for (int n = 0; n < storage->Data.Size; n++)
10119 {
10120 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
10121 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.
10122 }
10123 ImGui::TreePop();
10124 }
10125 };
10126
10127 Funcs::NodeWindows(g.Windows, "Windows");
10128 //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder");
10129 if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
10130 {
10131 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
10132 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
10133 ImGui::TreePop();
10134 }
10135
10136 // Details for Popups
10137 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
10138 {
10139 for (int i = 0; i < g.OpenPopupStack.Size; i++)
10140 {
10141 ImGuiWindow* window = g.OpenPopupStack[i].Window;
10142 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" : "");
10143 }
10144 ImGui::TreePop();
10145 }
10146
10147 // Details for TabBars
10148 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
10149 {
10150 for (int n = 0; n < g.TabBars.GetSize(); n++)
10151 Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
10152 ImGui::TreePop();
10153 }
10154
10155 // Details for Tables
10156 IM_UNUSED(trt_rects_names);
10157 IM_UNUSED(show_tables_rects);
10158 IM_UNUSED(show_tables_rect_type);
10159 #ifdef IMGUI_HAS_TABLE
10160 if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
10161 {
10162 for (int n = 0; n < g.Tables.GetSize(); n++)
10163 Funcs::NodeTable(g.Tables.GetByIndex(n));
10164 ImGui::TreePop();
10165 }
10166 #endif // #define IMGUI_HAS_TABLE
10167
10168 // Details for Docking
10169 #ifdef IMGUI_HAS_DOCK
10170 if (ImGui::TreeNode("Docking"))
10171 {
10172 ImGui::TreePop();
10173 }
10174 #endif // #define IMGUI_HAS_DOCK
10175
10176 // Misc Details
10177 if (ImGui::TreeNode("Internal state"))
10178 {
10179 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10180 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10181 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10182 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
10183 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]);
10184 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10185 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10186 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10187 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10188 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10189 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10190 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10191 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10192 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10193 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10194 ImGui::TreePop();
10195 }
10196
10197 // Tools
10198 if (ImGui::TreeNode("Tools"))
10199 {
10200 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10201 if (ImGui::Button("Item Picker.."))
10202 ImGui::DebugStartItemPicker();
10203 ImGui::SameLine();
10204 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.");
10205
10206 ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
10207 ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
10208 ImGui::SameLine();
10209 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
10210 show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count);
10211 if (show_windows_rects && g.NavWindow)
10212 {
10213 ImGui::BulletText("'%s':", g.NavWindow->Name);
10214 ImGui::Indent();
10215 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10216 {
10217 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10218 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]);
10219 }
10220 ImGui::Unindent();
10221 }
10222 ImGui::Checkbox("Show details when hovering ImDrawCmd node", &show_drawcmd_details);
10223 ImGui::TreePop();
10224 }
10225
10226 // Overlay: Display windows Rectangles and Begin Order
10227 if (show_windows_rects || show_windows_begin_order)
10228 {
10229 for (int n = 0; n < g.Windows.Size; n++)
10230 {
10231 ImGuiWindow* window = g.Windows[n];
10232 if (!window->WasActive)
10233 continue;
10234 ImDrawList* draw_list = GetForegroundDrawList(window);
10235 if (show_windows_rects)
10236 {
10237 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
10238 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10239 }
10240 if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10241 {
10242 char buf[32];
10243 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10244 float font_size = ImGui::GetFontSize();
10245 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10246 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10247 }
10248 }
10249 }
10250
10251 #ifdef IMGUI_HAS_TABLE
10252 // Overlay: Display Tables Rectangles
10253 if (show_tables_rects)
10254 {
10255 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++)
10256 {
10257 ImGuiTable* table = g.Tables.GetByIndex(table_n);
10258 }
10259 }
10260 #endif // #define IMGUI_HAS_TABLE
10261
10262 #ifdef IMGUI_HAS_DOCK
10263 // Overlay: Display Docking info
10264 if (show_docking_nodes && g.IO.KeyCtrl)
10265 {
10266 }
10267 #endif // #define IMGUI_HAS_DOCK
10268
10269 ImGui::End();
10270 }
10271
10272 #else
10273
ShowMetricsWindow(bool *)10274 void ImGui::ShowMetricsWindow(bool*) { }
10275
10276 #endif
10277
10278 //-----------------------------------------------------------------------------
10279
10280 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
10281 // 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.
10282 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
10283 #include "imgui_user.inl"
10284 #endif
10285
10286 //-----------------------------------------------------------------------------
10287
10288 #endif // #ifndef IMGUI_DISABLE
10289