1 // dear imgui, v1.74
2 // (main code and documentation)
3
4 // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code.
5 // Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase.
6 // Get latest version at https://github.com/ocornut/imgui
7 // Releases change-log at https://github.com/ocornut/imgui/releases
8 // Technical Support for Getting Started https://github.com/ocornut/imgui/wiki
9 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/2847
10
11 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
12 // See LICENSE.txt for copyright and licensing details (standard MIT License).
13 // This library is free but I need your support to sustain development and maintenance.
14 // Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README.
15 // Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui.
16
17 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
18 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
19 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
20 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
21 // to a better solution or official support for them.
22
23 /*
24
25 Index of this file:
26
27 DOCUMENTATION
28
29 - MISSION STATEMENT
30 - END-USER GUIDE
31 - PROGRAMMER GUIDE
32 - READ FIRST
33 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
34 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
35 - HOW A SIMPLE APPLICATION MAY LOOK LIKE (2 variations)
36 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
37 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
38 - API BREAKING CHANGES (read me when you update!)
39 - FREQUENTLY ASKED QUESTIONS (FAQ)
40 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
41
42 CODE
43 (search for "[SECTION]" in the code to find them)
44
45 // [SECTION] FORWARD DECLARATIONS
46 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
47 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
48 // [SECTION] MISC HELPERS/UTILITIES (Geomtry, String, Format, Hash, File functions)
49 // [SECTION] MISC HELPERS/UTILITIES (File functions)
50 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
51 // [SECTION] MISC HELPERS/UTILITIES (Color functions)
52 // [SECTION] ImGuiStorage
53 // [SECTION] ImGuiTextFilter
54 // [SECTION] ImGuiTextBuffer
55 // [SECTION] ImGuiListClipper
56 // [SECTION] RENDER HELPERS
57 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
58 // [SECTION] ERROR CHECKING
59 // [SECTION] SCROLLING
60 // [SECTION] TOOLTIPS
61 // [SECTION] POPUPS
62 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
63 // [SECTION] DRAG AND DROP
64 // [SECTION] LOGGING/CAPTURING
65 // [SECTION] SETTINGS
66 // [SECTION] PLATFORM DEPENDENT HELPERS
67 // [SECTION] METRICS/DEBUG WINDOW
68
69 */
70
71 //-----------------------------------------------------------------------------
72 // DOCUMENTATION
73 //-----------------------------------------------------------------------------
74
75 /*
76
77 MISSION STATEMENT
78 =================
79
80 - Easy to use to create code-driven and data-driven tools.
81 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
82 - Easy to hack and improve.
83 - Minimize screen real-estate usage.
84 - Minimize setup and maintenance.
85 - Minimize state storage on user side.
86 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
87 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
88 opening a tree node for the first time, etc. but a typical frame should not allocate anything).
89
90 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
91 - Doesn't look fancy, doesn't animate.
92 - Limited layout features, intricate layouts are typically crafted in code.
93
94
95 END-USER GUIDE
96 ==============
97
98 - Double-click on title bar to collapse window.
99 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
100 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
101 - Click and drag on any empty space to move window.
102 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
103 - CTRL+Click on a slider or drag box to input value as text.
104 - Use mouse wheel to scroll.
105 - Text editor:
106 - Hold SHIFT or use mouse to select text.
107 - CTRL+Left/Right to word jump.
108 - CTRL+Shift+Left/Right to select words.
109 - CTRL+A our Double-Click to select all.
110 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
111 - CTRL+Z,CTRL+Y to undo/redo.
112 - ESCAPE to revert text to its original value.
113 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
114 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
115 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
116 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
117
118
119 PROGRAMMER GUIDE
120 ================
121
122 READ FIRST
123 ----------
124 - Remember to read the FAQ (https://www.dearimgui.org/faq)
125 - 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
126 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
127 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
128 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
129 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
130 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
131 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
132 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,
133 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
134 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
135 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
136 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
137 If you get an assert, read the messages and comments around the assert.
138 - 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.
139 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
140 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
141 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
142 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
143
144 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
145 ----------------------------------------------
146 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
147 - Or maintain your own branch where you have imconfig.h modified.
148 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
149 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
150 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
151 likely be a comment about it. Please report any issue to the GitHub page!
152 - Try to keep your copy of dear imgui reasonably up to date.
153
154 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
155 ---------------------------------------------------------------
156 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
157 - Add the Dear ImGui source files to your projects or using your preferred build system.
158 It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
159 - 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.
160 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
161 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
162 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
163 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render().
164 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
165 - 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.
166
167 HOW A SIMPLE APPLICATION MAY LOOK LIKE
168 --------------------------------------
169 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
170
171 // Application init: create a dear imgui context, setup some options, load fonts
172 ImGui::CreateContext();
173 ImGuiIO& io = ImGui::GetIO();
174 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
175 // TODO: Fill optional fields of the io structure later.
176 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
177
178 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
179 ImGui_ImplWin32_Init(hwnd);
180 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
181
182 // Application main loop
183 while (true)
184 {
185 // Feed inputs to dear imgui, start new frame
186 ImGui_ImplDX11_NewFrame();
187 ImGui_ImplWin32_NewFrame();
188 ImGui::NewFrame();
189
190 // Any application code here
191 ImGui::Text("Hello, world!");
192
193 // Render dear imgui into screen
194 ImGui::Render();
195 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
196 g_pSwapChain->Present(1, 0);
197 }
198
199 // Shutdown
200 ImGui_ImplDX11_Shutdown();
201 ImGui_ImplWin32_Shutdown();
202 ImGui::DestroyContext();
203
204 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
205
206 // Application init: create a dear imgui context, setup some options, load fonts
207 ImGui::CreateContext();
208 ImGuiIO& io = ImGui::GetIO();
209 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
210 // TODO: Fill optional fields of the io structure later.
211 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
212
213 // Build and load the texture atlas into a texture
214 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
215 int width, height;
216 unsigned char* pixels = NULL;
217 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
218
219 // At this point you've got the texture data and you need to upload that your your graphic system:
220 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
221 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.
222 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
223 io.Fonts->TexID = (void*)texture;
224
225 // Application main loop
226 while (true)
227 {
228 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
229 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
230 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
231 io.DisplaySize.x = 1920.0f; // set the current display width
232 io.DisplaySize.y = 1280.0f; // set the current display height here
233 io.MousePos = my_mouse_pos; // set the mouse position
234 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
235 io.MouseDown[1] = my_mouse_buttons[1];
236
237 // Call NewFrame(), after this point you can use ImGui::* functions anytime
238 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere)
239 ImGui::NewFrame();
240
241 // Most of your application code here
242 ImGui::Text("Hello, world!");
243 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
244 MyGameRender(); // may use any Dear ImGui functions as well!
245
246 // Render dear imgui, swap buffers
247 // (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)
248 ImGui::EndFrame();
249 ImGui::Render();
250 ImDrawData* draw_data = ImGui::GetDrawData();
251 MyImGuiRenderFunction(draw_data);
252 SwapBuffers();
253 }
254
255 // Shutdown
256 ImGui::DestroyContext();
257
258 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
259 ---------------------------------------------
260 void void MyImGuiRenderFunction(ImDrawData* draw_data)
261 {
262 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
263 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
264 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
265 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
266 for (int n = 0; n < draw_data->CmdListsCount; n++)
267 {
268 const ImDrawList* cmd_list = draw_data->CmdLists[n];
269 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui
270 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui
271 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
272 {
273 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
274 if (pcmd->UserCallback)
275 {
276 pcmd->UserCallback(cmd_list, pcmd);
277 }
278 else
279 {
280 // The texture for the draw call is specified by pcmd->TextureId.
281 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.
282 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
283
284 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
285 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
286 // (some elements visible outside their bounds) but you can fix that once everything else works!
287 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
288 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
289 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
290 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
291 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
292 ImVec2 pos = draw_data->DisplayPos;
293 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));
294
295 // Render 'pcmd->ElemCount/3' indexed triangles.
296 // 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.
297 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
298 }
299 idx_buffer += pcmd->ElemCount;
300 }
301 }
302 }
303
304 - The examples/ folders contains many actual implementation of the pseudo-codes above.
305 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
306 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
307 rest of your application. In every cases you need to pass on the inputs to Dear ImGui.
308 - Refer to the FAQ for more information. Amusingly, it is called a FAQ because people frequently run into the same issues!
309
310 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
311 ------------------------------------------
312 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
313 - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
314 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
315 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
316 - Gamepad:
317 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
318 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
319 Note that io.NavInputs[] is cleared by EndFrame().
320 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
321 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
322 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
323 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.).
324 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
325 - 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
326 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
327 - Keyboard:
328 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
329 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
330 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
331 will be set. For more advanced uses, you may want to read from:
332 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
333 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
334 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
335 Please reach out if you think the game vs navigation input sharing could be improved.
336 - Mouse:
337 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
338 - 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.
339 - 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.
340 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
341 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.
342 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.
343 (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!)
344 (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
345 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
346
347
348 API BREAKING CHANGES
349 ====================
350
351 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
352 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.
353 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.
354 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
355
356 - 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.
357 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
358 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
359 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): Begin() (5 arguments signature), IsRootWindowOrAnyChildHovered(), AlignFirstTextHeightToWidgets(), SetNextWindowPosCenter(), ImFont::Glyph. See docs/Changelog.txt or grep this log for details and new names, or see how they were implemented until 1.73.
360 - 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.
361 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.
362 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).
363 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
364 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
365 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
366 - 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.
367 - 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
368 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.
369 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.
370 Please reach out if you are affected.
371 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
372 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
373 - 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.
374 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
375 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
376 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
377 - 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!
378 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
379 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
380 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
381 - 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.
382 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
383 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
384 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
385 - 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.
386 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
387 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
388 - 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.
389 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
390 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
391 - 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).
392 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
393 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
394 - 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.
395 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
396 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
397 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
398 - 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.).
399 old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports.
400 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
401 in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
402 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
403 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
404 - 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.
405 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
406 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.
407 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.
408 - 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",
409 consistent with other functions. Kept redirection functions (will obsolete).
410 - 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.
411 - 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).
412 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
413 - 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.
414 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
415 - 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.
416 - 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.
417 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
418 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
419 - removed Shutdown() function, as DestroyContext() serve this purpose.
420 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
421 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
422 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
423 - 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.
424 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
425 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
426 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
427 - 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.
428 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
429 - 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
430 - 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.
431 - 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.
432 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
433 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
434 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
435 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
436 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
437 - 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.
438 - 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.
439 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.
440 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
441 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
442 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
443 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
444 - 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.
445 - 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.
446 - 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.
447 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
448 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
449 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
450 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
451 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
452 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
453 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
454 - 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).
455 - 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)".
456 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
457 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
458 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
459 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
460 - 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.
461 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
462 - 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.
463 - 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).
464 - 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).
465 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
466 - 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.
467 - 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.
468 - 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))'
469 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
470 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
471 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
472 - 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().
473 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
474 - 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.
475 - 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.
476 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
477 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.
478 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:
479 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); }
480 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.
481 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
482 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
483 - 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).
484 - 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.
485 - 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).
486 - 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)
487 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
488 - 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.
489 - 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.
490 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
491 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
492 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
493 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.
494 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!
495 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
496 - 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.
497 - 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
498 - 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.
499 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
500 - 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.
501 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
502 - 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.
503 - the signature of the io.RenderDrawListsFn handler has changed!
504 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
505 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
506 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
507 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
508 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
509 - 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.
510 - 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!
511 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
512 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
513 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
514 - 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.
515 - 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
516 - 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!
517 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
518 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
519 - 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.
520 - 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.
521 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
522 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
523 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
524 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
525 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
526 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
527 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
528 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
529 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
530 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
531 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
532 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
533 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
534 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
535 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
536 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
537 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
538 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
539 font init: { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
540 became: { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
541 you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
542 it is now recommended that you sample the font texture with bilinear interpolation.
543 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
544 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
545 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
546 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
547 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
548 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
549 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
550 - 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)
551 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
552 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
553 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
554 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
555 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
556 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
557
558
559 FREQUENTLY ASKED QUESTIONS (FAQ)
560 ================================
561
562 Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer)
563 Some answers are copied down here to facilitate searching in code.
564
565 Q&A: Basics
566 ===========
567
568 Q: Where is the documentation?
569 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++.
570 - Run the examples/ and explore them.
571 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
572 - The demo covers most features of Dear ImGui, so you can read the code and see its output.
573 - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
574 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the examples/
575 folder to explain how to integrate Dear ImGui with your own engine/application.
576 - Your programming IDE is your friend, find the type or function declaration to find comments
577 associated to it.
578
579 Q: Which version should I get?
580 Q: Why the names "Dear ImGui" vs "ImGui"?
581 >> See https://www.dearimgui.org/faq
582
583 Q&A: Concerns
584 =============
585
586 Q: Who uses Dear ImGui?
587 Q: Can you create elaborate/serious tools with Dear ImGui?
588 Q: Can you reskin the look of Dear ImGui?
589 Q: Why using C++ (as opposed to C)?
590 >> See https://www.dearimgui.org/faq
591
592 Q&A: Integration
593 ================
594
595 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application?
596 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
597 - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application.
598 - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application.
599 - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS).
600 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
601 This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
602 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
603 It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs.
604 Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also
605 perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to UpdateHoveredWindowAndCaptureFlags().
606 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
607 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
608 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
609
610 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
611 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
612 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
613 >> See https://www.dearimgui.org/faq
614
615 Q&A: Usage
616 ----------
617
618 Q: Why are multiple widgets reacting when I interact with a single one?
619 Q: How can I have multiple widgets with the same label or with an empty label?
620 A: A primer on labels and the ID Stack...
621
622 Dear ImGui internally need to uniquely identify UI elements.
623 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
624 Interactive widgets (such as calls to Button buttons) need a unique ID.
625 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
626 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
627
628 - Unique ID are often derived from a string label:
629
630 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
631 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
632
633 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
634 two buttons labeled "OK" in different windows or different tree locations is fine.
635 We used "..." above to signify whatever was already pushed to the ID stack previously:
636
637 Begin("MyWindow");
638 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
639 End();
640 Begin("MyOtherWindow");
641 Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
642 End();
643
644 - If you have a same ID twice in the same location, you'll have a conflict:
645
646 Button("OK");
647 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
648
649 Fear not! this is easy to solve and there are many ways to solve it!
650
651 - Solving ID conflict in a simple/local context:
652 When passing a label you can optionally specify extra ID information within string itself.
653 Use "##" to pass a complement to the ID that won't be visible to the end-user.
654 This helps solving the simple collision cases when you know e.g. at compilation time which items
655 are going to be created:
656
657 Begin("MyWindow");
658 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
659 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
660 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
661 End();
662
663 - If you want to completely hide the label, but still need an ID:
664
665 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
666
667 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
668 you to animate labels. For example you may want to include varying information in a window title bar,
669 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
670
671 Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID")
672 Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different
673
674 sprintf(buf, "My game (%f FPS)###MyGame", fps);
675 Begin(buf); // Variable title, ID = hash of "MyGame"
676
677 - Solving ID conflict in a more general manner:
678 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
679 within the same window. This is the most convenient way of distinguishing ID when iterating and
680 creating many UI elements programmatically.
681 You can push a pointer, a string or an integer value into the ID stack.
682 Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
683 At each level of the stack we store the seed used for items at this level of the ID stack.
684
685 Begin("Window");
686 for (int i = 0; i < 100; i++)
687 {
688 PushID(i); // Push i to the id tack
689 Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click")
690 PopID();
691 }
692 for (int i = 0; i < 100; i++)
693 {
694 MyObject* obj = Objects[i];
695 PushID(obj);
696 Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click")
697 PopID();
698 }
699 for (int i = 0; i < 100; i++)
700 {
701 MyObject* obj = Objects[i];
702 PushID(obj->Name);
703 Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click")
704 PopID();
705 }
706 End();
707
708 - You can stack multiple prefixes into the ID stack:
709
710 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
711 PushID("node");
712 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
713 PushID(my_ptr);
714 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
715 PopID();
716 PopID();
717
718 - Tree nodes implicitly creates a scope for you by calling PushID().
719
720 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
721 if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
722 {
723 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
724 TreePop();
725 }
726
727 - When working with trees, ID are used to preserve the open/close state of each tree node.
728 Depending on your use cases you may want to use strings, indices or pointers as ID.
729 e.g. when following a single pointer that may change over time, using a static string as ID
730 will preserve your node open/closed state when the targeted object change.
731 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
732 node open/closed state differently. See what makes more sense in your situation!
733
734 Q: How can I display an image? What is ImTextureID, how does it works?
735 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
736
737 Q: How can I use my own math types instead of ImVec2/ImVec4?
738 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
739 Q: How can I display custom shapes? (using low-level ImDrawList API)
740 >> See https://www.dearimgui.org/faq
741
742 Q&A: Fonts, Text
743 ================
744
745 Q: How can I load a different font than the default?
746 Q: How can I easily use icons in my application?
747 Q: How can I load multiple fonts?
748 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
749 >> See https://www.dearimgui.org/faq and docs/FONTS.txt
750
751 Q&A: Community
752 ==============
753
754 Q: How can I help?
755 A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
756 and see how you want to help and can help!
757 - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
758 - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
759 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
760 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/2847). Visuals are ideal as they inspire other programmers.
761 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
762 - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately).
763
764 */
765
766 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
767 #define _CRT_SECURE_NO_WARNINGS
768 #endif
769
770 #include "imgui.h"
771 #ifndef IMGUI_DEFINE_MATH_OPERATORS
772 #define IMGUI_DEFINE_MATH_OPERATORS
773 #endif
774 #include "imgui_internal.h"
775
776 #include <ctype.h> // toupper
777 #include <stdio.h> // vsnprintf, sscanf, printf
778 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
779 #include <stddef.h> // intptr_t
780 #else
781 #include <stdint.h> // intptr_t
782 #endif
783
784 // Debug options
785 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL
786 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window
787 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file
788
789 // Visual Studio warnings
790 #ifdef _MSC_VER
791 #pragma warning (disable: 4127) // condition expression is constant
792 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
793 #endif
794
795 // Clang/GCC warnings with -Weverything
796 #if defined(__clang__)
797 #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!
798 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
799 #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.
800 #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.
801 #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.
802 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is.
803 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
804 #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.
805 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
806 #if __has_warning("-Wzero-as-null-pointer-constant")
807 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
808 #endif
809 #if __has_warning("-Wdouble-promotion")
810 #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.
811 #endif
812 #elif defined(__GNUC__)
813 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association.
814 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
815 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
816 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
817 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
818 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
819 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
820 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
821 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
822 #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
823 #endif
824
825 // 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.
826 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
827 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
828
829 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
830 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
831 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
832 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.
833
834 //-------------------------------------------------------------------------
835 // [SECTION] FORWARD DECLARATIONS
836 //-------------------------------------------------------------------------
837
838 static void SetCurrentWindow(ImGuiWindow* window);
839 static void FindHoveredWindow();
840 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
841 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
842
843 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
844 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
845
846 static ImRect GetViewportRect();
847
848 // Settings
849 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
850 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
851 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
852
853 // Platform Dependents default implementation for IO functions
854 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
855 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
856 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
857
858 namespace ImGui
859 {
860 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
861
862 // Navigation
863 static void NavUpdate();
864 static void NavUpdateWindowing();
865 static void NavUpdateWindowingOverlay();
866 static void NavUpdateMoveResult();
867 static float NavUpdatePageUpPageDown();
868 static inline void NavUpdateAnyRequestFlag();
869 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand);
870 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id);
871 static ImVec2 NavCalcPreferredRefPos();
872 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
873 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
874 static int FindWindowFocusIndex(ImGuiWindow* window);
875
876 // Error Checking
877 static void ErrorCheckEndFrame();
878 static void ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write);
879
880 // Misc
881 static void UpdateMouseInputs();
882 static void UpdateMouseWheel();
883 static bool UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
884 static void UpdateDebugToolItemPicker();
885 static void RenderWindowOuterBorders(ImGuiWindow* window);
886 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);
887 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
888
889 }
890
891 //-----------------------------------------------------------------------------
892 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
893 //-----------------------------------------------------------------------------
894
895 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
896 // ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
897 // 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call
898 // SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading.
899 // In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into.
900 // 2) Important: Dear ImGui functions are not thread-safe because of this pointer.
901 // If you want thread-safety to allow N threads to access N different contexts, you can:
902 // - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h:
903 // struct ImGuiContext;
904 // extern thread_local ImGuiContext* MyImGuiTLS;
905 // #define GImGui MyImGuiTLS
906 // 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.
907 // - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
908 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace.
909 #ifndef GImGui
910 ImGuiContext* GImGui = NULL;
911 #endif
912
913 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
914 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
915 // 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.
916 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)917 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
FreeWrapper(void * ptr,void * user_data)918 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }
919 #else
MallocWrapper(size_t size,void * user_data)920 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)921 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
922 #endif
923
924 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
925 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
926 static void* GImAllocatorUserData = NULL;
927
928 //-----------------------------------------------------------------------------
929 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
930 //-----------------------------------------------------------------------------
931
ImGuiStyle()932 ImGuiStyle::ImGuiStyle()
933 {
934 Alpha = 1.0f; // Global alpha applies to everything in ImGui
935 WindowPadding = ImVec2(8,8); // Padding within a window
936 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
937 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
938 WindowMinSize = ImVec2(32,32); // Minimum window size
939 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
940 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
941 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
942 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
943 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
944 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
945 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
946 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
947 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
948 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
949 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
950 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!
951 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
952 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
953 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
954 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
955 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
956 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
957 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
958 TabBorderSize = 0.0f; // Thickness of border around tabs.
959 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
960 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
961 SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text.
962 DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
963 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.
964 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
965 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
966 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
967 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.
968
969 // Default theme
970 ImGui::StyleColorsDark(this);
971 }
972
973 // 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.
974 // 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)975 void ImGuiStyle::ScaleAllSizes(float scale_factor)
976 {
977 WindowPadding = ImFloor(WindowPadding * scale_factor);
978 WindowRounding = ImFloor(WindowRounding * scale_factor);
979 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
980 ChildRounding = ImFloor(ChildRounding * scale_factor);
981 PopupRounding = ImFloor(PopupRounding * scale_factor);
982 FramePadding = ImFloor(FramePadding * scale_factor);
983 FrameRounding = ImFloor(FrameRounding * scale_factor);
984 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
985 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
986 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
987 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
988 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
989 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
990 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
991 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
992 GrabRounding = ImFloor(GrabRounding * scale_factor);
993 TabRounding = ImFloor(TabRounding * scale_factor);
994 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
995 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
996 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
997 }
998
ImGuiIO()999 ImGuiIO::ImGuiIO()
1000 {
1001 // Most fields are initialized with zero
1002 memset(this, 0, sizeof(*this));
1003
1004 // Settings
1005 ConfigFlags = ImGuiConfigFlags_None;
1006 BackendFlags = ImGuiBackendFlags_None;
1007 DisplaySize = ImVec2(-1.0f, -1.0f);
1008 DeltaTime = 1.0f/60.0f;
1009 IniSavingRate = 5.0f;
1010 IniFilename = "imgui.ini";
1011 LogFilename = "imgui_log.txt";
1012 MouseDoubleClickTime = 0.30f;
1013 MouseDoubleClickMaxDist = 6.0f;
1014 for (int i = 0; i < ImGuiKey_COUNT; i++)
1015 KeyMap[i] = -1;
1016 KeyRepeatDelay = 0.275f;
1017 KeyRepeatRate = 0.050f;
1018 UserData = NULL;
1019
1020 Fonts = NULL;
1021 FontGlobalScale = 1.0f;
1022 FontDefault = NULL;
1023 FontAllowUserScaling = false;
1024 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1025
1026 // Miscellaneous options
1027 MouseDrawCursor = false;
1028 #ifdef __APPLE__
1029 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1030 #else
1031 ConfigMacOSXBehaviors = false;
1032 #endif
1033 ConfigInputTextCursorBlink = true;
1034 ConfigWindowsResizeFromEdges = true;
1035 ConfigWindowsMoveFromTitleBarOnly = false;
1036 ConfigWindowsMemoryCompactTimer = 60.0f;
1037
1038 // Platform Functions
1039 BackendPlatformName = BackendRendererName = NULL;
1040 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1041 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1042 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1043 ClipboardUserData = NULL;
1044 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1045 ImeWindowHandle = NULL;
1046
1047 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1048 RenderDrawListsFn = NULL;
1049 #endif
1050
1051 // Input (NB: we already have memset zero the entire structure!)
1052 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1053 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1054 MouseDragThreshold = 6.0f;
1055 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1056 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1057 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1058 }
1059
1060 // Pass in translated ASCII characters for text input.
1061 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1062 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(unsigned int c)1063 void ImGuiIO::AddInputCharacter(unsigned int c)
1064 {
1065 if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1066 InputQueueCharacters.push_back((ImWchar)c);
1067 }
1068
AddInputCharactersUTF8(const char * utf8_chars)1069 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1070 {
1071 while (*utf8_chars != 0)
1072 {
1073 unsigned int c = 0;
1074 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1075 if (c > 0 && c <= IM_UNICODE_CODEPOINT_MAX)
1076 InputQueueCharacters.push_back((ImWchar)c);
1077 }
1078 }
1079
ClearInputCharacters()1080 void ImGuiIO::ClearInputCharacters()
1081 {
1082 InputQueueCharacters.resize(0);
1083 }
1084
1085 //-----------------------------------------------------------------------------
1086 // [SECTION] MISC HELPERS/UTILITIES (Geometry, String, Format, Hash, File functions)
1087 //-----------------------------------------------------------------------------
1088
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1089 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1090 {
1091 ImVec2 ap = p - a;
1092 ImVec2 ab_dir = b - a;
1093 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1094 if (dot < 0.0f)
1095 return a;
1096 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1097 if (dot > ab_len_sqr)
1098 return b;
1099 return a + ab_dir * dot / ab_len_sqr;
1100 }
1101
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1102 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1103 {
1104 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1105 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1106 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1107 return ((b1 == b2) && (b2 == b3));
1108 }
1109
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1110 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1111 {
1112 ImVec2 v0 = b - a;
1113 ImVec2 v1 = c - a;
1114 ImVec2 v2 = p - a;
1115 const float denom = v0.x * v1.y - v1.x * v0.y;
1116 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1117 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1118 out_u = 1.0f - out_v - out_w;
1119 }
1120
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1121 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1122 {
1123 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1124 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1125 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1126 float dist2_ab = ImLengthSqr(p - proj_ab);
1127 float dist2_bc = ImLengthSqr(p - proj_bc);
1128 float dist2_ca = ImLengthSqr(p - proj_ca);
1129 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1130 if (m == dist2_ab)
1131 return proj_ab;
1132 if (m == dist2_bc)
1133 return proj_bc;
1134 return proj_ca;
1135 }
1136
1137 // 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)1138 int ImStricmp(const char* str1, const char* str2)
1139 {
1140 int d;
1141 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1142 return d;
1143 }
1144
ImStrnicmp(const char * str1,const char * str2,size_t count)1145 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1146 {
1147 int d = 0;
1148 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1149 return d;
1150 }
1151
ImStrncpy(char * dst,const char * src,size_t count)1152 void ImStrncpy(char* dst, const char* src, size_t count)
1153 {
1154 if (count < 1)
1155 return;
1156 if (count > 1)
1157 strncpy(dst, src, count - 1);
1158 dst[count - 1] = 0;
1159 }
1160
ImStrdup(const char * str)1161 char* ImStrdup(const char* str)
1162 {
1163 size_t len = strlen(str);
1164 void* buf = IM_ALLOC(len + 1);
1165 return (char*)memcpy(buf, (const void*)str, len + 1);
1166 }
1167
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1168 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1169 {
1170 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1171 size_t src_size = strlen(src) + 1;
1172 if (dst_buf_size < src_size)
1173 {
1174 IM_FREE(dst);
1175 dst = (char*)IM_ALLOC(src_size);
1176 if (p_dst_size)
1177 *p_dst_size = src_size;
1178 }
1179 return (char*)memcpy(dst, (const void*)src, src_size);
1180 }
1181
ImStrchrRange(const char * str,const char * str_end,char c)1182 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1183 {
1184 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1185 return p;
1186 }
1187
ImStrlenW(const ImWchar * str)1188 int ImStrlenW(const ImWchar* str)
1189 {
1190 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit
1191 int n = 0;
1192 while (*str++) n++;
1193 return n;
1194 }
1195
1196 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1197 const char* ImStreolRange(const char* str, const char* str_end)
1198 {
1199 const char* p = (const char*)memchr(str, '\n', str_end - str);
1200 return p ? p : str_end;
1201 }
1202
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1203 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1204 {
1205 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1206 buf_mid_line--;
1207 return buf_mid_line;
1208 }
1209
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1210 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1211 {
1212 if (!needle_end)
1213 needle_end = needle + strlen(needle);
1214
1215 const char un0 = (char)toupper(*needle);
1216 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1217 {
1218 if (toupper(*haystack) == un0)
1219 {
1220 const char* b = needle + 1;
1221 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1222 if (toupper(*a) != toupper(*b))
1223 break;
1224 if (b == needle_end)
1225 return haystack;
1226 }
1227 haystack++;
1228 }
1229 return NULL;
1230 }
1231
1232 // 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)1233 void ImStrTrimBlanks(char* buf)
1234 {
1235 char* p = buf;
1236 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1237 p++;
1238 char* p_start = p;
1239 while (*p != 0) // Find end of string
1240 p++;
1241 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1242 p--;
1243 if (p_start != buf) // Copy memory if we had leading blanks
1244 memmove(buf, p_start, p - p_start);
1245 buf[p - p_start] = 0; // Zero terminate
1246 }
1247
ImStrSkipBlank(const char * str)1248 const char* ImStrSkipBlank(const char* str)
1249 {
1250 while (str[0] == ' ' || str[0] == '\t')
1251 str++;
1252 return str;
1253 }
1254
1255 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1256 // 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.
1257 // B) When buf==NULL vsnprintf() will return the output size.
1258 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1259
1260 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
1261 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1262 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
1263 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
1264 //#define IMGUI_USE_STB_SPRINTF
1265 #ifdef IMGUI_USE_STB_SPRINTF
1266 #define STB_SPRINTF_IMPLEMENTATION
1267 #include "stb_sprintf.h"
1268 #endif
1269
1270 #if defined(_MSC_VER) && !defined(vsnprintf)
1271 #define vsnprintf _vsnprintf
1272 #endif
1273
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1274 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1275 {
1276 va_list args;
1277 va_start(args, fmt);
1278 #ifdef IMGUI_USE_STB_SPRINTF
1279 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1280 #else
1281 int w = vsnprintf(buf, buf_size, fmt, args);
1282 #endif
1283 va_end(args);
1284 if (buf == NULL)
1285 return w;
1286 if (w == -1 || w >= (int)buf_size)
1287 w = (int)buf_size - 1;
1288 buf[w] = 0;
1289 return w;
1290 }
1291
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1292 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1293 {
1294 #ifdef IMGUI_USE_STB_SPRINTF
1295 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1296 #else
1297 int w = vsnprintf(buf, buf_size, fmt, args);
1298 #endif
1299 if (buf == NULL)
1300 return w;
1301 if (w == -1 || w >= (int)buf_size)
1302 w = (int)buf_size - 1;
1303 buf[w] = 0;
1304 return w;
1305 }
1306 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
1307
1308 // CRC32 needs a 1KB lookup table (not cache friendly)
1309 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
1310 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
1311 static const ImU32 GCrc32LookupTable[256] =
1312 {
1313 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
1314 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
1315 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
1316 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
1317 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
1318 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
1319 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
1320 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
1321 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
1322 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
1323 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
1324 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
1325 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
1326 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
1327 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
1328 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
1329 };
1330
1331 // Known size hash
1332 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
1333 // 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)1334 ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
1335 {
1336 ImU32 crc = ~seed;
1337 const unsigned char* data = (const unsigned char*)data_p;
1338 const ImU32* crc32_lut = GCrc32LookupTable;
1339 while (data_size-- != 0)
1340 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
1341 return ~crc;
1342 }
1343
1344 // Zero-terminated string hash, with support for ### to reset back to seed value
1345 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1346 // Because this syntax is rarely used we are optimizing for the common case.
1347 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1348 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
1349 // 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)1350 ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed)
1351 {
1352 seed = ~seed;
1353 ImU32 crc = seed;
1354 const unsigned char* data = (const unsigned char*)data_p;
1355 const ImU32* crc32_lut = GCrc32LookupTable;
1356 if (data_size != 0)
1357 {
1358 while (data_size-- != 0)
1359 {
1360 unsigned char c = *data++;
1361 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
1362 crc = seed;
1363 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1364 }
1365 }
1366 else
1367 {
1368 while (unsigned char c = *data++)
1369 {
1370 if (c == '#' && data[0] == '#' && data[1] == '#')
1371 crc = seed;
1372 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1373 }
1374 }
1375 return ~crc;
1376 }
1377
1378 //-----------------------------------------------------------------------------
1379 // [SECTION] MISC HELPERS/UTILITIES (File functions)
1380 //-----------------------------------------------------------------------------
1381
1382 // Default file functions
1383 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
ImFileOpen(const char * filename,const char * mode)1384 ImFileHandle ImFileOpen(const char* filename, const char* mode)
1385 {
1386 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)
1387 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
1388 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1389 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1390 ImVector<ImWchar> buf;
1391 buf.resize(filename_wsize + mode_wsize);
1392 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1393 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1394 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1395 #else
1396 return fopen(filename, mode);
1397 #endif
1398 }
1399
1400 // 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)1401 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; }
ImFileGetSize(ImFileHandle f)1402 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)1403 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)1404 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); }
1405 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
1406
1407 // Helper: Load file content into memory
1408 // 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)1409 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
1410 {
1411 IM_ASSERT(filename && mode);
1412 if (out_file_size)
1413 *out_file_size = 0;
1414
1415 ImFileHandle f;
1416 if ((f = ImFileOpen(filename, mode)) == NULL)
1417 return NULL;
1418
1419 size_t file_size = (size_t)ImFileGetSize(f);
1420 if (file_size == (size_t)-1)
1421 {
1422 ImFileClose(f);
1423 return NULL;
1424 }
1425
1426 void* file_data = IM_ALLOC(file_size + padding_bytes);
1427 if (file_data == NULL)
1428 {
1429 ImFileClose(f);
1430 return NULL;
1431 }
1432 if (ImFileRead(file_data, 1, file_size, f) != file_size)
1433 {
1434 ImFileClose(f);
1435 IM_FREE(file_data);
1436 return NULL;
1437 }
1438 if (padding_bytes > 0)
1439 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1440
1441 ImFileClose(f);
1442 if (out_file_size)
1443 *out_file_size = file_size;
1444
1445 return file_data;
1446 }
1447
1448 //-----------------------------------------------------------------------------
1449 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1450 //-----------------------------------------------------------------------------
1451
1452 // Convert UTF-8 to 32-bit character, process single character input.
1453 // Based on stb_from_utf8() from github.com/nothings/stb/
1454 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1455 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1456 {
1457 unsigned int c = (unsigned int)-1;
1458 const unsigned char* str = (const unsigned char*)in_text;
1459 if (!(*str & 0x80))
1460 {
1461 c = (unsigned int)(*str++);
1462 *out_char = c;
1463 return 1;
1464 }
1465 if ((*str & 0xe0) == 0xc0)
1466 {
1467 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1468 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1469 if (*str < 0xc2) return 2;
1470 c = (unsigned int)((*str++ & 0x1f) << 6);
1471 if ((*str & 0xc0) != 0x80) return 2;
1472 c += (*str++ & 0x3f);
1473 *out_char = c;
1474 return 2;
1475 }
1476 if ((*str & 0xf0) == 0xe0)
1477 {
1478 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1479 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1480 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1481 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1482 c = (unsigned int)((*str++ & 0x0f) << 12);
1483 if ((*str & 0xc0) != 0x80) return 3;
1484 c += (unsigned int)((*str++ & 0x3f) << 6);
1485 if ((*str & 0xc0) != 0x80) return 3;
1486 c += (*str++ & 0x3f);
1487 *out_char = c;
1488 return 3;
1489 }
1490 if ((*str & 0xf8) == 0xf0)
1491 {
1492 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string
1493 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1494 if (*str > 0xf4) return 4;
1495 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1496 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1497 c = (unsigned int)((*str++ & 0x07) << 18);
1498 if ((*str & 0xc0) != 0x80) return 4;
1499 c += (unsigned int)((*str++ & 0x3f) << 12);
1500 if ((*str & 0xc0) != 0x80) return 4;
1501 c += (unsigned int)((*str++ & 0x3f) << 6);
1502 if ((*str & 0xc0) != 0x80) return 4;
1503 c += (*str++ & 0x3f);
1504 // utf-8 encodings of values used in surrogate pairs are invalid
1505 if ((c & 0xFFFFF800) == 0xD800) return 4;
1506 *out_char = c;
1507 return 4;
1508 }
1509 *out_char = 0;
1510 return 0;
1511 }
1512
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1513 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1514 {
1515 ImWchar* buf_out = buf;
1516 ImWchar* buf_end = buf + buf_size;
1517 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1518 {
1519 unsigned int c;
1520 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1521 if (c == 0)
1522 break;
1523 if (c <= IM_UNICODE_CODEPOINT_MAX) // FIXME: Losing characters that don't fit in 2 bytes
1524 *buf_out++ = (ImWchar)c;
1525 }
1526 *buf_out = 0;
1527 if (in_text_remaining)
1528 *in_text_remaining = in_text;
1529 return (int)(buf_out - buf);
1530 }
1531
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1532 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1533 {
1534 int char_count = 0;
1535 while ((!in_text_end || in_text < in_text_end) && *in_text)
1536 {
1537 unsigned int c;
1538 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1539 if (c == 0)
1540 break;
1541 if (c <= IM_UNICODE_CODEPOINT_MAX)
1542 char_count++;
1543 }
1544 return char_count;
1545 }
1546
1547 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1548 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1549 {
1550 if (c < 0x80)
1551 {
1552 buf[0] = (char)c;
1553 return 1;
1554 }
1555 if (c < 0x800)
1556 {
1557 if (buf_size < 2) return 0;
1558 buf[0] = (char)(0xc0 + (c >> 6));
1559 buf[1] = (char)(0x80 + (c & 0x3f));
1560 return 2;
1561 }
1562 if (c >= 0xdc00 && c < 0xe000)
1563 {
1564 return 0;
1565 }
1566 if (c >= 0xd800 && c < 0xdc00)
1567 {
1568 if (buf_size < 4) return 0;
1569 buf[0] = (char)(0xf0 + (c >> 18));
1570 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1571 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1572 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1573 return 4;
1574 }
1575 //else if (c < 0x10000)
1576 {
1577 if (buf_size < 3) return 0;
1578 buf[0] = (char)(0xe0 + (c >> 12));
1579 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1580 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1581 return 3;
1582 }
1583 }
1584
1585 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1586 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1587 {
1588 unsigned int dummy = 0;
1589 return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1590 }
1591
ImTextCountUtf8BytesFromChar(unsigned int c)1592 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1593 {
1594 if (c < 0x80) return 1;
1595 if (c < 0x800) return 2;
1596 if (c >= 0xdc00 && c < 0xe000) return 0;
1597 if (c >= 0xd800 && c < 0xdc00) return 4;
1598 return 3;
1599 }
1600
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1601 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1602 {
1603 char* buf_out = buf;
1604 const char* buf_end = buf + buf_size;
1605 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1606 {
1607 unsigned int c = (unsigned int)(*in_text++);
1608 if (c < 0x80)
1609 *buf_out++ = (char)c;
1610 else
1611 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1612 }
1613 *buf_out = 0;
1614 return (int)(buf_out - buf);
1615 }
1616
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1617 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1618 {
1619 int bytes_count = 0;
1620 while ((!in_text_end || in_text < in_text_end) && *in_text)
1621 {
1622 unsigned int c = (unsigned int)(*in_text++);
1623 if (c < 0x80)
1624 bytes_count++;
1625 else
1626 bytes_count += ImTextCountUtf8BytesFromChar(c);
1627 }
1628 return bytes_count;
1629 }
1630
1631 //-----------------------------------------------------------------------------
1632 // [SECTION] MISC HELPERS/UTILTIES (Color functions)
1633 // Note: The Convert functions are early design which are not consistent with other API.
1634 //-----------------------------------------------------------------------------
1635
ColorConvertU32ToFloat4(ImU32 in)1636 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1637 {
1638 float s = 1.0f/255.0f;
1639 return ImVec4(
1640 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1641 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1642 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1643 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1644 }
1645
ColorConvertFloat4ToU32(const ImVec4 & in)1646 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1647 {
1648 ImU32 out;
1649 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1650 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1651 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1652 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1653 return out;
1654 }
1655
1656 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1657 // 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)1658 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1659 {
1660 float K = 0.f;
1661 if (g < b)
1662 {
1663 ImSwap(g, b);
1664 K = -1.f;
1665 }
1666 if (r < g)
1667 {
1668 ImSwap(r, g);
1669 K = -2.f / 6.f - K;
1670 }
1671
1672 const float chroma = r - (g < b ? g : b);
1673 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1674 out_s = chroma / (r + 1e-20f);
1675 out_v = r;
1676 }
1677
1678 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1679 // 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)1680 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1681 {
1682 if (s == 0.0f)
1683 {
1684 // gray
1685 out_r = out_g = out_b = v;
1686 return;
1687 }
1688
1689 h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1690 int i = (int)h;
1691 float f = h - (float)i;
1692 float p = v * (1.0f - s);
1693 float q = v * (1.0f - s * f);
1694 float t = v * (1.0f - s * (1.0f - f));
1695
1696 switch (i)
1697 {
1698 case 0: out_r = v; out_g = t; out_b = p; break;
1699 case 1: out_r = q; out_g = v; out_b = p; break;
1700 case 2: out_r = p; out_g = v; out_b = t; break;
1701 case 3: out_r = p; out_g = q; out_b = v; break;
1702 case 4: out_r = t; out_g = p; out_b = v; break;
1703 case 5: default: out_r = v; out_g = p; out_b = q; break;
1704 }
1705 }
1706
GetColorU32(ImGuiCol idx,float alpha_mul)1707 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1708 {
1709 ImGuiStyle& style = GImGui->Style;
1710 ImVec4 c = style.Colors[idx];
1711 c.w *= style.Alpha * alpha_mul;
1712 return ColorConvertFloat4ToU32(c);
1713 }
1714
GetColorU32(const ImVec4 & col)1715 ImU32 ImGui::GetColorU32(const ImVec4& col)
1716 {
1717 ImGuiStyle& style = GImGui->Style;
1718 ImVec4 c = col;
1719 c.w *= style.Alpha;
1720 return ColorConvertFloat4ToU32(c);
1721 }
1722
GetStyleColorVec4(ImGuiCol idx)1723 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1724 {
1725 ImGuiStyle& style = GImGui->Style;
1726 return style.Colors[idx];
1727 }
1728
GetColorU32(ImU32 col)1729 ImU32 ImGui::GetColorU32(ImU32 col)
1730 {
1731 float style_alpha = GImGui->Style.Alpha;
1732 if (style_alpha >= 1.0f)
1733 return col;
1734 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1735 a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1736 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1737 }
1738
1739 //-----------------------------------------------------------------------------
1740 // [SECTION] ImGuiStorage
1741 // Helper: Key->value storage
1742 //-----------------------------------------------------------------------------
1743
1744 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair> & data,ImGuiID key)1745 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)
1746 {
1747 ImGuiStorage::ImGuiStoragePair* first = data.Data;
1748 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;
1749 size_t count = (size_t)(last - first);
1750 while (count > 0)
1751 {
1752 size_t count2 = count >> 1;
1753 ImGuiStorage::ImGuiStoragePair* mid = first + count2;
1754 if (mid->key < key)
1755 {
1756 first = ++mid;
1757 count -= count2 + 1;
1758 }
1759 else
1760 {
1761 count = count2;
1762 }
1763 }
1764 return first;
1765 }
1766
1767 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1768 void ImGuiStorage::BuildSortByKey()
1769 {
1770 struct StaticFunc
1771 {
1772 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1773 {
1774 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1775 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;
1776 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;
1777 return 0;
1778 }
1779 };
1780 if (Data.Size > 1)
1781 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID);
1782 }
1783
GetInt(ImGuiID key,int default_val) const1784 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1785 {
1786 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1787 if (it == Data.end() || it->key != key)
1788 return default_val;
1789 return it->val_i;
1790 }
1791
GetBool(ImGuiID key,bool default_val) const1792 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1793 {
1794 return GetInt(key, default_val ? 1 : 0) != 0;
1795 }
1796
GetFloat(ImGuiID key,float default_val) const1797 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1798 {
1799 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1800 if (it == Data.end() || it->key != key)
1801 return default_val;
1802 return it->val_f;
1803 }
1804
GetVoidPtr(ImGuiID key) const1805 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1806 {
1807 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);
1808 if (it == Data.end() || it->key != key)
1809 return NULL;
1810 return it->val_p;
1811 }
1812
1813 // 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)1814 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1815 {
1816 ImGuiStoragePair* it = LowerBound(Data, key);
1817 if (it == Data.end() || it->key != key)
1818 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1819 return &it->val_i;
1820 }
1821
GetBoolRef(ImGuiID key,bool default_val)1822 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1823 {
1824 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1825 }
1826
GetFloatRef(ImGuiID key,float default_val)1827 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1828 {
1829 ImGuiStoragePair* it = LowerBound(Data, key);
1830 if (it == Data.end() || it->key != key)
1831 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1832 return &it->val_f;
1833 }
1834
GetVoidPtrRef(ImGuiID key,void * default_val)1835 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1836 {
1837 ImGuiStoragePair* it = LowerBound(Data, key);
1838 if (it == Data.end() || it->key != key)
1839 it = Data.insert(it, ImGuiStoragePair(key, default_val));
1840 return &it->val_p;
1841 }
1842
1843 // 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)1844 void ImGuiStorage::SetInt(ImGuiID key, int val)
1845 {
1846 ImGuiStoragePair* it = LowerBound(Data, key);
1847 if (it == Data.end() || it->key != key)
1848 {
1849 Data.insert(it, ImGuiStoragePair(key, val));
1850 return;
1851 }
1852 it->val_i = val;
1853 }
1854
SetBool(ImGuiID key,bool val)1855 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1856 {
1857 SetInt(key, val ? 1 : 0);
1858 }
1859
SetFloat(ImGuiID key,float val)1860 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1861 {
1862 ImGuiStoragePair* it = LowerBound(Data, key);
1863 if (it == Data.end() || it->key != key)
1864 {
1865 Data.insert(it, ImGuiStoragePair(key, val));
1866 return;
1867 }
1868 it->val_f = val;
1869 }
1870
SetVoidPtr(ImGuiID key,void * val)1871 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1872 {
1873 ImGuiStoragePair* it = LowerBound(Data, key);
1874 if (it == Data.end() || it->key != key)
1875 {
1876 Data.insert(it, ImGuiStoragePair(key, val));
1877 return;
1878 }
1879 it->val_p = val;
1880 }
1881
SetAllInt(int v)1882 void ImGuiStorage::SetAllInt(int v)
1883 {
1884 for (int i = 0; i < Data.Size; i++)
1885 Data[i].val_i = v;
1886 }
1887
1888 //-----------------------------------------------------------------------------
1889 // [SECTION] ImGuiTextFilter
1890 //-----------------------------------------------------------------------------
1891
1892 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1893 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1894 {
1895 if (default_filter)
1896 {
1897 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1898 Build();
1899 }
1900 else
1901 {
1902 InputBuf[0] = 0;
1903 CountGrep = 0;
1904 }
1905 }
1906
Draw(const char * label,float width)1907 bool ImGuiTextFilter::Draw(const char* label, float width)
1908 {
1909 if (width != 0.0f)
1910 ImGui::SetNextItemWidth(width);
1911 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1912 if (value_changed)
1913 Build();
1914 return value_changed;
1915 }
1916
split(char separator,ImVector<ImGuiTextRange> * out) const1917 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
1918 {
1919 out->resize(0);
1920 const char* wb = b;
1921 const char* we = wb;
1922 while (we < e)
1923 {
1924 if (*we == separator)
1925 {
1926 out->push_back(ImGuiTextRange(wb, we));
1927 wb = we + 1;
1928 }
1929 we++;
1930 }
1931 if (wb != we)
1932 out->push_back(ImGuiTextRange(wb, we));
1933 }
1934
Build()1935 void ImGuiTextFilter::Build()
1936 {
1937 Filters.resize(0);
1938 ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1939 input_range.split(',', &Filters);
1940
1941 CountGrep = 0;
1942 for (int i = 0; i != Filters.Size; i++)
1943 {
1944 ImGuiTextRange& f = Filters[i];
1945 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1946 f.b++;
1947 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1948 f.e--;
1949 if (f.empty())
1950 continue;
1951 if (Filters[i].b[0] != '-')
1952 CountGrep += 1;
1953 }
1954 }
1955
PassFilter(const char * text,const char * text_end) const1956 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1957 {
1958 if (Filters.empty())
1959 return true;
1960
1961 if (text == NULL)
1962 text = "";
1963
1964 for (int i = 0; i != Filters.Size; i++)
1965 {
1966 const ImGuiTextRange& f = Filters[i];
1967 if (f.empty())
1968 continue;
1969 if (f.b[0] == '-')
1970 {
1971 // Subtract
1972 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
1973 return false;
1974 }
1975 else
1976 {
1977 // Grep
1978 if (ImStristr(text, text_end, f.b, f.e) != NULL)
1979 return true;
1980 }
1981 }
1982
1983 // Implicit * grep
1984 if (CountGrep == 0)
1985 return true;
1986
1987 return false;
1988 }
1989
1990 //-----------------------------------------------------------------------------
1991 // [SECTION] ImGuiTextBuffer
1992 //-----------------------------------------------------------------------------
1993
1994 // On some platform vsnprintf() takes va_list by reference and modifies it.
1995 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1996 #ifndef va_copy
1997 #if defined(__GNUC__) || defined(__clang__)
1998 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1999 #else
2000 #define va_copy(dest, src) (dest = src)
2001 #endif
2002 #endif
2003
2004 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2005
append(const char * str,const char * str_end)2006 void ImGuiTextBuffer::append(const char* str, const char* str_end)
2007 {
2008 int len = str_end ? (int)(str_end - str) : (int)strlen(str);
2009
2010 // Add zero-terminator the first time
2011 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2012 const int needed_sz = write_off + len;
2013 if (write_off + len >= Buf.Capacity)
2014 {
2015 int new_capacity = Buf.Capacity * 2;
2016 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2017 }
2018
2019 Buf.resize(needed_sz);
2020 memcpy(&Buf[write_off - 1], str, (size_t)len);
2021 Buf[write_off - 1 + len] = 0;
2022 }
2023
appendf(const char * fmt,...)2024 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2025 {
2026 va_list args;
2027 va_start(args, fmt);
2028 appendfv(fmt, args);
2029 va_end(args);
2030 }
2031
2032 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2033 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2034 {
2035 va_list args_copy;
2036 va_copy(args_copy, args);
2037
2038 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2039 if (len <= 0)
2040 {
2041 va_end(args_copy);
2042 return;
2043 }
2044
2045 // Add zero-terminator the first time
2046 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2047 const int needed_sz = write_off + len;
2048 if (write_off + len >= Buf.Capacity)
2049 {
2050 int new_capacity = Buf.Capacity * 2;
2051 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
2052 }
2053
2054 Buf.resize(needed_sz);
2055 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2056 va_end(args_copy);
2057 }
2058
2059 //-----------------------------------------------------------------------------
2060 // [SECTION] ImGuiListClipper
2061 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed
2062 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO)
2063 //-----------------------------------------------------------------------------
2064
2065 // Helper to calculate coarse clipping of large list of evenly sized items.
2066 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
2067 // 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)2068 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
2069 {
2070 ImGuiContext& g = *GImGui;
2071 ImGuiWindow* window = g.CurrentWindow;
2072 if (g.LogEnabled)
2073 {
2074 // If logging is active, do not perform any clipping
2075 *out_items_display_start = 0;
2076 *out_items_display_end = items_count;
2077 return;
2078 }
2079 if (window->SkipItems)
2080 {
2081 *out_items_display_start = *out_items_display_end = 0;
2082 return;
2083 }
2084
2085 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
2086 ImRect unclipped_rect = window->ClipRect;
2087 if (g.NavMoveRequest)
2088 unclipped_rect.Add(g.NavScoringRectScreen);
2089
2090 const ImVec2 pos = window->DC.CursorPos;
2091 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
2092 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
2093
2094 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
2095 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
2096 start--;
2097 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
2098 end++;
2099
2100 start = ImClamp(start, 0, items_count);
2101 end = ImClamp(end + 1, start, items_count);
2102 *out_items_display_start = start;
2103 *out_items_display_end = end;
2104 }
2105
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2106 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2107 {
2108 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2109 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2110 // The clipper should probably have a 4th step to display the last item in a regular manner.
2111 ImGuiContext& g = *GImGui;
2112 ImGuiWindow* window = g.CurrentWindow;
2113 window->DC.CursorPos.y = pos_y;
2114 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y);
2115 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.
2116 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.
2117 if (ImGuiColumns* columns = window->DC.CurrentColumns)
2118 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2119 }
2120
2121 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2122 // Use case B: Begin() called from constructor with items_height>0
2123 // 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)2124 void ImGuiListClipper::Begin(int count, float items_height)
2125 {
2126 ImGuiContext& g = *GImGui;
2127 ImGuiWindow* window = g.CurrentWindow;
2128
2129 StartPosY = window->DC.CursorPos.y;
2130 ItemsHeight = items_height;
2131 ItemsCount = count;
2132 StepNo = 0;
2133 DisplayEnd = DisplayStart = -1;
2134 if (ItemsHeight > 0.0f)
2135 {
2136 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2137 if (DisplayStart > 0)
2138 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2139 StepNo = 2;
2140 }
2141 }
2142
End()2143 void ImGuiListClipper::End()
2144 {
2145 if (ItemsCount < 0)
2146 return;
2147 // 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.
2148 if (ItemsCount < INT_MAX)
2149 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2150 ItemsCount = -1;
2151 StepNo = 3;
2152 }
2153
Step()2154 bool ImGuiListClipper::Step()
2155 {
2156 ImGuiContext& g = *GImGui;
2157 ImGuiWindow* window = g.CurrentWindow;
2158
2159 if (ItemsCount == 0 || window->SkipItems)
2160 {
2161 ItemsCount = -1;
2162 return false;
2163 }
2164 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.
2165 {
2166 DisplayStart = 0;
2167 DisplayEnd = 1;
2168 StartPosY = window->DC.CursorPos.y;
2169 StepNo = 1;
2170 return true;
2171 }
2172 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.
2173 {
2174 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2175 float items_height = window->DC.CursorPos.y - StartPosY;
2176 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2177 Begin(ItemsCount - 1, items_height);
2178 DisplayStart++;
2179 DisplayEnd++;
2180 StepNo = 3;
2181 return true;
2182 }
2183 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.
2184 {
2185 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2186 StepNo = 3;
2187 return true;
2188 }
2189 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.
2190 End();
2191 return false;
2192 }
2193
2194 //-----------------------------------------------------------------------------
2195 // [SECTION] RENDER HELPERS
2196 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2197 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2198 //-----------------------------------------------------------------------------
2199
FindRenderedTextEnd(const char * text,const char * text_end)2200 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2201 {
2202 const char* text_display_end = text;
2203 if (!text_end)
2204 text_end = (const char*)-1;
2205
2206 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2207 text_display_end++;
2208 return text_display_end;
2209 }
2210
2211 // Internal ImGui functions to render text
2212 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2213 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2214 {
2215 ImGuiContext& g = *GImGui;
2216 ImGuiWindow* window = g.CurrentWindow;
2217
2218 // Hide anything after a '##' string
2219 const char* text_display_end;
2220 if (hide_text_after_hash)
2221 {
2222 text_display_end = FindRenderedTextEnd(text, text_end);
2223 }
2224 else
2225 {
2226 if (!text_end)
2227 text_end = text + strlen(text); // FIXME-OPT
2228 text_display_end = text_end;
2229 }
2230
2231 if (text != text_display_end)
2232 {
2233 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2234 if (g.LogEnabled)
2235 LogRenderedText(&pos, text, text_display_end);
2236 }
2237 }
2238
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2239 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2240 {
2241 ImGuiContext& g = *GImGui;
2242 ImGuiWindow* window = g.CurrentWindow;
2243
2244 if (!text_end)
2245 text_end = text + strlen(text); // FIXME-OPT
2246
2247 if (text != text_end)
2248 {
2249 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2250 if (g.LogEnabled)
2251 LogRenderedText(&pos, text, text_end);
2252 }
2253 }
2254
2255 // Default clip_rect uses (pos_min,pos_max)
2256 // 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)2257 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)
2258 {
2259 // Perform CPU side clipping for single clipped element to avoid using scissor state
2260 ImVec2 pos = pos_min;
2261 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2262
2263 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2264 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2265 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2266 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2267 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2268
2269 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2270 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2271 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2272
2273 // Render
2274 if (need_clipping)
2275 {
2276 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2277 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2278 }
2279 else
2280 {
2281 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2282 }
2283 }
2284
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)2285 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)
2286 {
2287 // Hide anything after a '##' string
2288 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2289 const int text_len = (int)(text_display_end - text);
2290 if (text_len == 0)
2291 return;
2292
2293 ImGuiContext& g = *GImGui;
2294 ImGuiWindow* window = g.CurrentWindow;
2295 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2296 if (g.LogEnabled)
2297 LogRenderedText(&pos_min, text, text_display_end);
2298 }
2299
2300
2301 // Another overly complex function until we reorganize everything into a nice all-in-one helper.
2302 // 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.
2303 // 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)2304 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)
2305 {
2306 ImGuiContext& g = *GImGui;
2307 if (text_end_full == NULL)
2308 text_end_full = FindRenderedTextEnd(text);
2309 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
2310
2311 //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));
2312 //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));
2313 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
2314 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
2315 if (text_size.x > pos_max.x - pos_min.x)
2316 {
2317 // Hello wo...
2318 // | | |
2319 // min max ellipsis_max
2320 // <-> this is generally some padding value
2321
2322 const ImFont* font = draw_list->_Data->Font;
2323 const float font_size = draw_list->_Data->FontSize;
2324 const char* text_end_ellipsis = NULL;
2325
2326 ImWchar ellipsis_char = font->EllipsisChar;
2327 int ellipsis_char_count = 1;
2328 if (ellipsis_char == (ImWchar)-1)
2329 {
2330 ellipsis_char = (ImWchar)'.';
2331 ellipsis_char_count = 3;
2332 }
2333 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
2334
2335 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side
2336 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
2337
2338 if (ellipsis_char_count > 1)
2339 {
2340 // Full ellipsis size without free spacing after it.
2341 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
2342 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
2343 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
2344 }
2345
2346 // We can now claim the space between pos_max.x and ellipsis_max.x
2347 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f);
2348 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
2349 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
2350 {
2351 // Always display at least 1 character if there's no room for character + ellipsis
2352 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);
2353 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;
2354 }
2355 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
2356 {
2357 // 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)
2358 text_end_ellipsis--;
2359 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
2360 }
2361
2362 // Render text, render ellipsis
2363 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
2364 float ellipsis_x = pos_min.x + text_size_clipped_x;
2365 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x)
2366 for (int i = 0; i < ellipsis_char_count; i++)
2367 {
2368 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
2369 ellipsis_x += ellipsis_glyph_width;
2370 }
2371 }
2372 else
2373 {
2374 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
2375 }
2376
2377 if (g.LogEnabled)
2378 LogRenderedText(&pos_min, text, text_end_full);
2379 }
2380
2381 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2382 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2383 {
2384 ImGuiContext& g = *GImGui;
2385 ImGuiWindow* window = g.CurrentWindow;
2386 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2387 const float border_size = g.Style.FrameBorderSize;
2388 if (border && border_size > 0.0f)
2389 {
2390 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2391 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2392 }
2393 }
2394
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2395 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2396 {
2397 ImGuiContext& g = *GImGui;
2398 ImGuiWindow* window = g.CurrentWindow;
2399 const float border_size = g.Style.FrameBorderSize;
2400 if (border_size > 0.0f)
2401 {
2402 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2403 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2404 }
2405 }
2406
2407 // 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)2408 void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
2409 {
2410 const float h = draw_list->_Data->FontSize * 1.00f;
2411 float r = h * 0.40f * scale;
2412 ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
2413
2414 ImVec2 a, b, c;
2415 switch (dir)
2416 {
2417 case ImGuiDir_Up:
2418 case ImGuiDir_Down:
2419 if (dir == ImGuiDir_Up) r = -r;
2420 a = ImVec2(+0.000f,+0.750f) * r;
2421 b = ImVec2(-0.866f,-0.750f) * r;
2422 c = ImVec2(+0.866f,-0.750f) * r;
2423 break;
2424 case ImGuiDir_Left:
2425 case ImGuiDir_Right:
2426 if (dir == ImGuiDir_Left) r = -r;
2427 a = ImVec2(+0.750f,+0.000f) * r;
2428 b = ImVec2(-0.750f,+0.866f) * r;
2429 c = ImVec2(-0.750f,-0.866f) * r;
2430 break;
2431 case ImGuiDir_None:
2432 case ImGuiDir_COUNT:
2433 IM_ASSERT(0);
2434 break;
2435 }
2436 draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
2437 }
2438
RenderBullet(ImDrawList * draw_list,ImVec2 pos,ImU32 col)2439 void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
2440 {
2441 draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);
2442 }
2443
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2444 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2445 {
2446 ImGuiContext& g = *GImGui;
2447 ImGuiWindow* window = g.CurrentWindow;
2448
2449 float thickness = ImMax(sz / 5.0f, 1.0f);
2450 sz -= thickness*0.5f;
2451 pos += ImVec2(thickness*0.25f, thickness*0.25f);
2452
2453 float third = sz / 3.0f;
2454 float bx = pos.x + third;
2455 float by = pos.y + sz - third*0.5f;
2456 window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2457 window->DrawList->PathLineTo(ImVec2(bx, by));
2458 window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2459 window->DrawList->PathStroke(col, false, thickness);
2460 }
2461
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2462 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2463 {
2464 ImGuiContext& g = *GImGui;
2465 if (id != g.NavId)
2466 return;
2467 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2468 return;
2469 ImGuiWindow* window = g.CurrentWindow;
2470 if (window->DC.NavHideHighlightOneFrame)
2471 return;
2472
2473 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2474 ImRect display_rect = bb;
2475 display_rect.ClipWith(window->ClipRect);
2476 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2477 {
2478 const float THICKNESS = 2.0f;
2479 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2480 display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2481 bool fully_visible = window->ClipRect.Contains(display_rect);
2482 if (!fully_visible)
2483 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2484 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);
2485 if (!fully_visible)
2486 window->DrawList->PopClipRect();
2487 }
2488 if (flags & ImGuiNavHighlightFlags_TypeThin)
2489 {
2490 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2491 }
2492 }
2493
2494 //-----------------------------------------------------------------------------
2495 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2496 //-----------------------------------------------------------------------------
2497
2498 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2499 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2500 : DrawListInst(&context->DrawListSharedData)
2501 {
2502 Name = ImStrdup(name);
2503 ID = ImHashStr(name);
2504 IDStack.push_back(ID);
2505 Flags = ImGuiWindowFlags_None;
2506 Pos = ImVec2(0.0f, 0.0f);
2507 Size = SizeFull = ImVec2(0.0f, 0.0f);
2508 ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f);
2509 WindowPadding = ImVec2(0.0f, 0.0f);
2510 WindowRounding = 0.0f;
2511 WindowBorderSize = 0.0f;
2512 NameBufLen = (int)strlen(name) + 1;
2513 MoveId = GetID("#MOVE");
2514 ChildId = 0;
2515 Scroll = ImVec2(0.0f, 0.0f);
2516 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2517 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2518 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2519 ScrollbarX = ScrollbarY = false;
2520 Active = WasActive = false;
2521 WriteAccessed = false;
2522 Collapsed = false;
2523 WantCollapseToggle = false;
2524 SkipItems = false;
2525 Appearing = false;
2526 Hidden = false;
2527 HasCloseButton = false;
2528 ResizeBorderHeld = -1;
2529 BeginCount = 0;
2530 BeginOrderWithinParent = -1;
2531 BeginOrderWithinContext = -1;
2532 PopupId = 0;
2533 AutoFitFramesX = AutoFitFramesY = -1;
2534 AutoFitChildAxises = 0x00;
2535 AutoFitOnlyGrows = false;
2536 AutoPosLastDirection = ImGuiDir_None;
2537 HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0;
2538 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2539 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2540
2541 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.
2542
2543 LastFrameActive = -1;
2544 LastTimeActive = -1.0f;
2545 ItemWidthDefault = 0.0f;
2546 FontWindowScale = 1.0f;
2547 SettingsOffset = -1;
2548
2549 DrawList = &DrawListInst;
2550 DrawList->_OwnerName = Name;
2551 ParentWindow = NULL;
2552 RootWindow = NULL;
2553 RootWindowForTitleBarHighlight = NULL;
2554 RootWindowForNav = NULL;
2555
2556 NavLastIds[0] = NavLastIds[1] = 0;
2557 NavRectRel[0] = NavRectRel[1] = ImRect();
2558 NavLastChildNavWindow = NULL;
2559
2560 MemoryCompacted = false;
2561 MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0;
2562 }
2563
~ImGuiWindow()2564 ImGuiWindow::~ImGuiWindow()
2565 {
2566 IM_ASSERT(DrawList == &DrawListInst);
2567 IM_DELETE(Name);
2568 for (int i = 0; i != ColumnsStorage.Size; i++)
2569 ColumnsStorage[i].~ImGuiColumns();
2570 }
2571
GetID(const char * str,const char * str_end)2572 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2573 {
2574 ImGuiID seed = IDStack.back();
2575 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2576 ImGui::KeepAliveID(id);
2577 return id;
2578 }
2579
GetID(const void * ptr)2580 ImGuiID ImGuiWindow::GetID(const void* ptr)
2581 {
2582 ImGuiID seed = IDStack.back();
2583 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
2584 ImGui::KeepAliveID(id);
2585 return id;
2586 }
2587
GetID(int n)2588 ImGuiID ImGuiWindow::GetID(int n)
2589 {
2590 ImGuiID seed = IDStack.back();
2591 ImGuiID id = ImHashData(&n, sizeof(n), seed);
2592 ImGui::KeepAliveID(id);
2593 return id;
2594 }
2595
GetIDNoKeepAlive(const char * str,const char * str_end)2596 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2597 {
2598 ImGuiID seed = IDStack.back();
2599 return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
2600 }
2601
GetIDNoKeepAlive(const void * ptr)2602 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2603 {
2604 ImGuiID seed = IDStack.back();
2605 return ImHashData(&ptr, sizeof(void*), seed);
2606 }
2607
GetIDNoKeepAlive(int n)2608 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n)
2609 {
2610 ImGuiID seed = IDStack.back();
2611 return ImHashData(&n, sizeof(n), seed);
2612 }
2613
2614 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2615 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2616 {
2617 ImGuiID seed = IDStack.back();
2618 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) };
2619 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
2620 ImGui::KeepAliveID(id);
2621 return id;
2622 }
2623
SetCurrentWindow(ImGuiWindow * window)2624 static void SetCurrentWindow(ImGuiWindow* window)
2625 {
2626 ImGuiContext& g = *GImGui;
2627 g.CurrentWindow = window;
2628 if (window)
2629 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2630 }
2631
2632 // Free up/compact internal window buffers, we can use this when a window becomes unused.
2633 // This is currently unused by the library, but you may call this yourself for easy GC.
2634 // Not freed:
2635 // - ImGuiWindow, ImGuiWindowSettings, Name
2636 // - StateStorage, ColumnsStorage (may hold useful data)
2637 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
GcCompactTransientWindowBuffers(ImGuiWindow * window)2638 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
2639 {
2640 window->MemoryCompacted = true;
2641 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
2642 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
2643 window->IDStack.clear();
2644 window->DrawList->ClearFreeMemory();
2645 window->DC.ChildWindows.clear();
2646 window->DC.ItemFlagsStack.clear();
2647 window->DC.ItemWidthStack.clear();
2648 window->DC.TextWrapPosStack.clear();
2649 window->DC.GroupStack.clear();
2650 }
2651
GcAwakeTransientWindowBuffers(ImGuiWindow * window)2652 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
2653 {
2654 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
2655 // The other buffers tends to amortize much faster.
2656 window->MemoryCompacted = false;
2657 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
2658 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
2659 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
2660 }
2661
SetNavID(ImGuiID id,int nav_layer)2662 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2663 {
2664 ImGuiContext& g = *GImGui;
2665 IM_ASSERT(g.NavWindow);
2666 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2667 g.NavId = id;
2668 g.NavWindow->NavLastIds[nav_layer] = id;
2669 }
2670
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2671 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2672 {
2673 ImGuiContext& g = *GImGui;
2674 SetNavID(id, nav_layer);
2675 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2676 g.NavMousePosDirty = true;
2677 g.NavDisableHighlight = false;
2678 g.NavDisableMouseHover = true;
2679 }
2680
SetActiveID(ImGuiID id,ImGuiWindow * window)2681 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2682 {
2683 ImGuiContext& g = *GImGui;
2684 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2685 if (g.ActiveIdIsJustActivated)
2686 {
2687 g.ActiveIdTimer = 0.0f;
2688 g.ActiveIdHasBeenPressedBefore = false;
2689 g.ActiveIdHasBeenEditedBefore = false;
2690 if (id != 0)
2691 {
2692 g.LastActiveId = id;
2693 g.LastActiveIdTimer = 0.0f;
2694 }
2695 }
2696 g.ActiveId = id;
2697 g.ActiveIdAllowOverlap = false;
2698 g.ActiveIdWindow = window;
2699 g.ActiveIdHasBeenEditedThisFrame = false;
2700 if (id)
2701 {
2702 g.ActiveIdIsAlive = id;
2703 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2704 }
2705
2706 // Clear declaration of inputs claimed by the widget
2707 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
2708 g.ActiveIdUsingNavDirMask = 0x00;
2709 g.ActiveIdUsingNavInputMask = 0x00;
2710 g.ActiveIdUsingKeyInputMask = 0x00;
2711 }
2712
2713 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2714 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2715 {
2716 ImGuiContext& g = *GImGui;
2717 IM_ASSERT(id != 0);
2718
2719 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2720 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2721 if (g.NavWindow != window)
2722 g.NavInitRequest = false;
2723 g.NavId = id;
2724 g.NavWindow = window;
2725 g.NavLayer = nav_layer;
2726 window->NavLastIds[nav_layer] = id;
2727 if (window->DC.LastItemId == id)
2728 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2729
2730 if (g.ActiveIdSource == ImGuiInputSource_Nav)
2731 g.NavDisableMouseHover = true;
2732 else
2733 g.NavDisableHighlight = true;
2734 }
2735
ClearActiveID()2736 void ImGui::ClearActiveID()
2737 {
2738 SetActiveID(0, NULL);
2739 }
2740
SetHoveredID(ImGuiID id)2741 void ImGui::SetHoveredID(ImGuiID id)
2742 {
2743 ImGuiContext& g = *GImGui;
2744 g.HoveredId = id;
2745 g.HoveredIdAllowOverlap = false;
2746 if (id != 0 && g.HoveredIdPreviousFrame != id)
2747 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2748 }
2749
GetHoveredID()2750 ImGuiID ImGui::GetHoveredID()
2751 {
2752 ImGuiContext& g = *GImGui;
2753 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2754 }
2755
KeepAliveID(ImGuiID id)2756 void ImGui::KeepAliveID(ImGuiID id)
2757 {
2758 ImGuiContext& g = *GImGui;
2759 if (g.ActiveId == id)
2760 g.ActiveIdIsAlive = id;
2761 if (g.ActiveIdPreviousFrame == id)
2762 g.ActiveIdPreviousFrameIsAlive = true;
2763 }
2764
MarkItemEdited(ImGuiID id)2765 void ImGui::MarkItemEdited(ImGuiID id)
2766 {
2767 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2768 // 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.
2769 ImGuiContext& g = *GImGui;
2770 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2771 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out.
2772 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2773 g.ActiveIdHasBeenEditedThisFrame = true;
2774 g.ActiveIdHasBeenEditedBefore = true;
2775 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2776 }
2777
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2778 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2779 {
2780 // An active popup disable hovering on other windows (apart from its own children)
2781 // FIXME-OPT: This could be cached/stored within the window.
2782 ImGuiContext& g = *GImGui;
2783 if (g.NavWindow)
2784 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2785 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2786 {
2787 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2788 // NB: The order of those two tests is important because Modal windows are also Popups.
2789 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2790 return false;
2791 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2792 return false;
2793 }
2794
2795 return true;
2796 }
2797
2798 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_baseline_y)2799 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
2800 {
2801 ImGuiContext& g = *GImGui;
2802 ImGuiWindow* window = g.CurrentWindow;
2803 if (window->SkipItems)
2804 return;
2805
2806 // We increase the height in this function to accommodate for baseline offset.
2807 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
2808 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
2809 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
2810 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y);
2811
2812 // Always align ourselves on pixel boundaries
2813 //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]
2814 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
2815 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y;
2816 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line
2817 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line
2818 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2819 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2820 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2821
2822 window->DC.PrevLineSize.y = line_height;
2823 window->DC.CurrLineSize.y = 0.0f;
2824 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
2825 window->DC.CurrLineTextBaseOffset = 0.0f;
2826
2827 // Horizontal layout mode
2828 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2829 SameLine();
2830 }
2831
ItemSize(const ImRect & bb,float text_baseline_y)2832 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y)
2833 {
2834 ItemSize(bb.GetSize(), text_baseline_y);
2835 }
2836
2837 // Declare item bounding box for clipping and interaction.
2838 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2839 // 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)2840 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2841 {
2842 ImGuiContext& g = *GImGui;
2843 ImGuiWindow* window = g.CurrentWindow;
2844
2845 if (id != 0)
2846 {
2847 // Navigation processing runs prior to clipping early-out
2848 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2849 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
2850 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
2851 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2852 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
2853 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
2854 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
2855 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
2856 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2857 if (g.NavId == id || g.NavAnyRequest)
2858 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2859 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2860 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2861
2862 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd()
2863 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
2864 if (id == g.DebugItemPickerBreakID)
2865 {
2866 IM_DEBUG_BREAK();
2867 g.DebugItemPickerBreakID = 0;
2868 }
2869 #endif
2870 }
2871
2872 window->DC.LastItemId = id;
2873 window->DC.LastItemRect = bb;
2874 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2875 g.NextItemData.Flags = ImGuiNextItemDataFlags_None;
2876
2877 #ifdef IMGUI_ENABLE_TEST_ENGINE
2878 if (id != 0)
2879 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id);
2880 #endif
2881
2882 // Clipping test
2883 const bool is_clipped = IsClippedEx(bb, id, false);
2884 if (is_clipped)
2885 return false;
2886 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2887
2888 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2889 if (IsMouseHoveringRect(bb.Min, bb.Max))
2890 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2891 return true;
2892 }
2893
2894 // This is roughly matching the behavior of internal-facing ItemHoverable()
2895 // - 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()
2896 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2897 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2898 {
2899 ImGuiContext& g = *GImGui;
2900 ImGuiWindow* window = g.CurrentWindow;
2901 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2902 return IsItemFocused();
2903
2904 // Test for bounding box overlap, as updated as ItemAdd()
2905 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2906 return false;
2907 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2908
2909 // Test if we are hovering the right window (our window could be behind another window)
2910 // [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.
2911 // 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.
2912 //if (g.HoveredWindow != window)
2913 // return false;
2914 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2915 return false;
2916
2917 // Test if another item is active (e.g. being dragged)
2918 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2919 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2920 return false;
2921
2922 // Test if interactions on this window are blocked by an active popup or modal.
2923 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
2924 if (!IsWindowContentHoverable(window, flags))
2925 return false;
2926
2927 // Test if the item is disabled
2928 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2929 return false;
2930
2931 // Special handling for the dummy item after Begin() which represent the title bar or tab.
2932 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
2933 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2934 return false;
2935 return true;
2936 }
2937
2938 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2939 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2940 {
2941 ImGuiContext& g = *GImGui;
2942 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2943 return false;
2944
2945 ImGuiWindow* window = g.CurrentWindow;
2946 if (g.HoveredWindow != window)
2947 return false;
2948 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2949 return false;
2950 if (!IsMouseHoveringRect(bb.Min, bb.Max))
2951 return false;
2952 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2953 return false;
2954 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2955 return false;
2956
2957 SetHoveredID(id);
2958
2959 // [DEBUG] Item Picker tool!
2960 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making
2961 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered
2962 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost.
2963 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd().
2964 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
2965 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
2966 if (g.DebugItemPickerBreakID == id)
2967 IM_DEBUG_BREAK();
2968
2969 return true;
2970 }
2971
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2972 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2973 {
2974 ImGuiContext& g = *GImGui;
2975 ImGuiWindow* window = g.CurrentWindow;
2976 if (!bb.Overlaps(window->ClipRect))
2977 if (id == 0 || id != g.ActiveId)
2978 if (clip_even_when_logged || !g.LogEnabled)
2979 return true;
2980 return false;
2981 }
2982
2983 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out.
FocusableItemRegister(ImGuiWindow * window,ImGuiID id)2984 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id)
2985 {
2986 ImGuiContext& g = *GImGui;
2987
2988 // Increment counters
2989 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2990 window->DC.FocusCounterAll++;
2991 if (is_tab_stop)
2992 window->DC.FocusCounterTab++;
2993
2994 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item.
2995 // (Note that we can always TAB out of a widget that doesn't allow tabbing in)
2996 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL)
2997 {
2998 g.FocusRequestNextWindow = window;
2999 g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (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.
3000 }
3001
3002 // Handle focus requests
3003 if (g.FocusRequestCurrWindow == window)
3004 {
3005 if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll)
3006 return true;
3007 if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab)
3008 {
3009 g.NavJustTabbedId = id;
3010 return true;
3011 }
3012
3013 // If another item is about to be focused, we clear our own active id
3014 if (g.ActiveId == id)
3015 ClearActiveID();
3016 }
3017
3018 return false;
3019 }
3020
FocusableItemUnregister(ImGuiWindow * window)3021 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
3022 {
3023 window->DC.FocusCounterAll--;
3024 window->DC.FocusCounterTab--;
3025 }
3026
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)3027 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
3028 {
3029 if (wrap_pos_x < 0.0f)
3030 return 0.0f;
3031
3032 ImGuiWindow* window = GImGui->CurrentWindow;
3033 if (wrap_pos_x == 0.0f)
3034 wrap_pos_x = window->WorkRect.Max.x;
3035 else if (wrap_pos_x > 0.0f)
3036 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
3037
3038 return ImMax(wrap_pos_x - pos.x, 1.0f);
3039 }
3040
3041 // IM_ALLOC() == ImGui::MemAlloc()
MemAlloc(size_t size)3042 void* ImGui::MemAlloc(size_t size)
3043 {
3044 if (ImGuiContext* ctx = GImGui)
3045 ctx->IO.MetricsActiveAllocations++;
3046 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
3047 }
3048
3049 // IM_FREE() == ImGui::MemFree()
MemFree(void * ptr)3050 void ImGui::MemFree(void* ptr)
3051 {
3052 if (ptr)
3053 if (ImGuiContext* ctx = GImGui)
3054 ctx->IO.MetricsActiveAllocations--;
3055 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
3056 }
3057
GetClipboardText()3058 const char* ImGui::GetClipboardText()
3059 {
3060 ImGuiContext& g = *GImGui;
3061 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : "";
3062 }
3063
SetClipboardText(const char * text)3064 void ImGui::SetClipboardText(const char* text)
3065 {
3066 ImGuiContext& g = *GImGui;
3067 if (g.IO.SetClipboardTextFn)
3068 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);
3069 }
3070
GetVersion()3071 const char* ImGui::GetVersion()
3072 {
3073 return IMGUI_VERSION;
3074 }
3075
3076 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3077 // 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()3078 ImGuiContext* ImGui::GetCurrentContext()
3079 {
3080 return GImGui;
3081 }
3082
SetCurrentContext(ImGuiContext * ctx)3083 void ImGui::SetCurrentContext(ImGuiContext* ctx)
3084 {
3085 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3086 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3087 #else
3088 GImGui = ctx;
3089 #endif
3090 }
3091
3092 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.
3093 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
3094 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code
3095 // may see different structures than what imgui.cpp sees, which is problematic.
3096 // 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)3097 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)
3098 {
3099 bool error = false;
3100 if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
3101 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
3102 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
3103 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
3104 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
3105 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
3106 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
3107 return !error;
3108 }
3109
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)3110 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
3111 {
3112 GImAllocatorAllocFunc = alloc_func;
3113 GImAllocatorFreeFunc = free_func;
3114 GImAllocatorUserData = user_data;
3115 }
3116
CreateContext(ImFontAtlas * shared_font_atlas)3117 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
3118 {
3119 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
3120 if (GImGui == NULL)
3121 SetCurrentContext(ctx);
3122 Initialize(ctx);
3123 return ctx;
3124 }
3125
DestroyContext(ImGuiContext * ctx)3126 void ImGui::DestroyContext(ImGuiContext* ctx)
3127 {
3128 if (ctx == NULL)
3129 ctx = GImGui;
3130 Shutdown(ctx);
3131 if (GImGui == ctx)
3132 SetCurrentContext(NULL);
3133 IM_DELETE(ctx);
3134 }
3135
GetIO()3136 ImGuiIO& ImGui::GetIO()
3137 {
3138 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3139 return GImGui->IO;
3140 }
3141
GetStyle()3142 ImGuiStyle& ImGui::GetStyle()
3143 {
3144 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3145 return GImGui->Style;
3146 }
3147
3148 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()3149 ImDrawData* ImGui::GetDrawData()
3150 {
3151 ImGuiContext& g = *GImGui;
3152 return g.DrawData.Valid ? &g.DrawData : NULL;
3153 }
3154
GetTime()3155 double ImGui::GetTime()
3156 {
3157 return GImGui->Time;
3158 }
3159
GetFrameCount()3160 int ImGui::GetFrameCount()
3161 {
3162 return GImGui->FrameCount;
3163 }
3164
GetBackgroundDrawList()3165 ImDrawList* ImGui::GetBackgroundDrawList()
3166 {
3167 return &GImGui->BackgroundDrawList;
3168 }
3169
GetForegroundDrawList()3170 ImDrawList* ImGui::GetForegroundDrawList()
3171 {
3172 return &GImGui->ForegroundDrawList;
3173 }
3174
GetDrawListSharedData()3175 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3176 {
3177 return &GImGui->DrawListSharedData;
3178 }
3179
StartMouseMovingWindow(ImGuiWindow * window)3180 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3181 {
3182 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3183 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3184 // This is because we want ActiveId to be set even when the window is not permitted to move.
3185 ImGuiContext& g = *GImGui;
3186 FocusWindow(window);
3187 SetActiveID(window->MoveId, window);
3188 g.NavDisableHighlight = true;
3189 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3190
3191 bool can_move_window = true;
3192 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3193 can_move_window = false;
3194 if (can_move_window)
3195 g.MovingWindow = window;
3196 }
3197
3198 // Handle mouse moving window
3199 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3200 void ImGui::UpdateMouseMovingWindowNewFrame()
3201 {
3202 ImGuiContext& g = *GImGui;
3203 if (g.MovingWindow != NULL)
3204 {
3205 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3206 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3207 KeepAliveID(g.ActiveId);
3208 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3209 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3210 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3211 {
3212 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3213 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3214 {
3215 MarkIniSettingsDirty(moving_window);
3216 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3217 }
3218 FocusWindow(g.MovingWindow);
3219 }
3220 else
3221 {
3222 ClearActiveID();
3223 g.MovingWindow = NULL;
3224 }
3225 }
3226 else
3227 {
3228 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3229 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3230 {
3231 KeepAliveID(g.ActiveId);
3232 if (!g.IO.MouseDown[0])
3233 ClearActiveID();
3234 }
3235 }
3236 }
3237
3238 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3239 void ImGui::UpdateMouseMovingWindowEndFrame()
3240 {
3241 // Initiate moving window
3242 ImGuiContext& g = *GImGui;
3243 if (g.ActiveId != 0 || g.HoveredId != 0)
3244 return;
3245
3246 // Unless we just made a window/popup appear
3247 if (g.NavWindow && g.NavWindow->Appearing)
3248 return;
3249
3250 // Click to focus window and start moving (after we're done with all our widgets)
3251 if (g.IO.MouseClicked[0])
3252 {
3253 if (g.HoveredRootWindow != NULL)
3254 {
3255 StartMouseMovingWindow(g.HoveredWindow);
3256 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3257 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3258 g.MovingWindow = NULL;
3259 }
3260 else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL)
3261 {
3262 // Clicking on void disable focus
3263 FocusWindow(NULL);
3264 }
3265 }
3266
3267 // With right mouse button we close popups without changing focus based on where the mouse is aimed
3268 // Instead, focus will be restored to the window under the bottom-most closed popup.
3269 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
3270 if (g.IO.MouseClicked[1])
3271 {
3272 // Find the top-most window between HoveredWindow and the top-most Modal Window.
3273 // This is where we can trim the popup stack.
3274 ImGuiWindow* modal = GetTopMostPopupModal();
3275 bool hovered_window_above_modal = false;
3276 if (modal == NULL)
3277 hovered_window_above_modal = true;
3278 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3279 {
3280 ImGuiWindow* window = g.Windows[i];
3281 if (window == modal)
3282 break;
3283 if (window == g.HoveredWindow)
3284 hovered_window_above_modal = true;
3285 }
3286 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
3287 }
3288 }
3289
IsWindowActiveAndVisible(ImGuiWindow * window)3290 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3291 {
3292 return (window->Active) && (!window->Hidden);
3293 }
3294
UpdateMouseInputs()3295 static void ImGui::UpdateMouseInputs()
3296 {
3297 ImGuiContext& g = *GImGui;
3298
3299 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3300 if (IsMousePosValid(&g.IO.MousePos))
3301 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3302
3303 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3304 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3305 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3306 else
3307 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3308 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3309 g.NavDisableMouseHover = false;
3310
3311 g.IO.MousePosPrev = g.IO.MousePos;
3312 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3313 {
3314 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3315 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3316 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3317 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;
3318 g.IO.MouseDoubleClicked[i] = false;
3319 if (g.IO.MouseClicked[i])
3320 {
3321 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3322 {
3323 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3324 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3325 g.IO.MouseDoubleClicked[i] = true;
3326 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
3327 }
3328 else
3329 {
3330 g.IO.MouseClickedTime[i] = g.Time;
3331 }
3332 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3333 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i];
3334 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3335 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3336 }
3337 else if (g.IO.MouseDown[i])
3338 {
3339 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3340 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3341 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3342 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);
3343 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);
3344 }
3345 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i])
3346 g.IO.MouseDownWasDoubleClick[i] = false;
3347 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3348 g.NavDisableMouseHover = false;
3349 }
3350 }
3351
StartLockWheelingWindow(ImGuiWindow * window)3352 static void StartLockWheelingWindow(ImGuiWindow* window)
3353 {
3354 ImGuiContext& g = *GImGui;
3355 if (g.WheelingWindow == window)
3356 return;
3357 g.WheelingWindow = window;
3358 g.WheelingWindowRefMousePos = g.IO.MousePos;
3359 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER;
3360 }
3361
UpdateMouseWheel()3362 void ImGui::UpdateMouseWheel()
3363 {
3364 ImGuiContext& g = *GImGui;
3365
3366 // Reset the locked window if we move the mouse or after the timer elapses
3367 if (g.WheelingWindow != NULL)
3368 {
3369 g.WheelingWindowTimer -= g.IO.DeltaTime;
3370 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
3371 g.WheelingWindowTimer = 0.0f;
3372 if (g.WheelingWindowTimer <= 0.0f)
3373 {
3374 g.WheelingWindow = NULL;
3375 g.WheelingWindowTimer = 0.0f;
3376 }
3377 }
3378
3379 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3380 return;
3381
3382 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
3383 if (!window || window->Collapsed)
3384 return;
3385
3386 // Zoom / Scale window
3387 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
3388 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3389 {
3390 StartLockWheelingWindow(window);
3391 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3392 const float scale = new_font_scale / window->FontWindowScale;
3393 window->FontWindowScale = new_font_scale;
3394 if (!(window->Flags & ImGuiWindowFlags_ChildWindow))
3395 {
3396 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3397 SetWindowPos(window, window->Pos + offset, 0);
3398 window->Size = ImFloor(window->Size * scale);
3399 window->SizeFull = ImFloor(window->SizeFull * scale);
3400 }
3401 return;
3402 }
3403
3404 // Mouse wheel scrolling
3405 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent
3406
3407 // Vertical Mouse Wheel scrolling
3408 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f;
3409 if (wheel_y != 0.0f && !g.IO.KeyCtrl)
3410 {
3411 StartLockWheelingWindow(window);
3412 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3413 window = window->ParentWindow;
3414 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3415 {
3416 float max_step = window->InnerRect.GetHeight() * 0.67f;
3417 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
3418 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
3419 }
3420 }
3421
3422 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held
3423 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;
3424 if (wheel_x != 0.0f && !g.IO.KeyCtrl)
3425 {
3426 StartLockWheelingWindow(window);
3427 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))))
3428 window = window->ParentWindow;
3429 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
3430 {
3431 float max_step = window->InnerRect.GetWidth() * 0.67f;
3432 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
3433 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
3434 }
3435 }
3436 }
3437
3438 // 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()3439 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3440 {
3441 ImGuiContext& g = *GImGui;
3442
3443 // Find the window hovered by mouse:
3444 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3445 // - 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.
3446 // - 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.
3447 FindHoveredWindow();
3448
3449 // Modal windows prevents cursor from hovering behind them.
3450 ImGuiWindow* modal_window = GetTopMostPopupModal();
3451 if (modal_window)
3452 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3453 g.HoveredRootWindow = g.HoveredWindow = NULL;
3454
3455 // Disabled mouse?
3456 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3457 g.HoveredWindow = g.HoveredRootWindow = NULL;
3458
3459 // 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.
3460 int mouse_earliest_button_down = -1;
3461 bool mouse_any_down = false;
3462 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3463 {
3464 if (g.IO.MouseClicked[i])
3465 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3466 mouse_any_down |= g.IO.MouseDown[i];
3467 if (g.IO.MouseDown[i])
3468 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3469 mouse_earliest_button_down = i;
3470 }
3471 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3472
3473 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3474 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3475 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3476 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3477 g.HoveredWindow = g.HoveredRootWindow = NULL;
3478
3479 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app)
3480 if (g.WantCaptureMouseNextFrame != -1)
3481 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3482 else
3483 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3484
3485 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app)
3486 if (g.WantCaptureKeyboardNextFrame != -1)
3487 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3488 else
3489 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3490 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3491 g.IO.WantCaptureKeyboard = true;
3492
3493 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3494 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3495 }
3496
NewFrameSanityChecks()3497 static void NewFrameSanityChecks()
3498 {
3499 ImGuiContext& g = *GImGui;
3500
3501 // Check user data
3502 // (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)
3503 IM_ASSERT(g.Initialized);
3504 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
3505 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3506 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
3507 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3508 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3509 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
3510 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)!");
3511 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
3512 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
3513 for (int n = 0; n < ImGuiKey_COUNT; n++)
3514 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)");
3515
3516 // 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)
3517 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3518 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3519
3520 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3521 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3522 g.IO.ConfigWindowsResizeFromEdges = false;
3523 }
3524
NewFrame()3525 void ImGui::NewFrame()
3526 {
3527 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3528 ImGuiContext& g = *GImGui;
3529
3530 #ifdef IMGUI_ENABLE_TEST_ENGINE
3531 ImGuiTestEngineHook_PreNewFrame(&g);
3532 #endif
3533
3534 // Check and assert for various common IO and Configuration mistakes
3535 NewFrameSanityChecks();
3536
3537 // Load settings on first frame (if not explicitly loaded manually before)
3538 if (!g.SettingsLoaded)
3539 {
3540 IM_ASSERT(g.SettingsWindows.empty());
3541 if (g.IO.IniFilename)
3542 LoadIniSettingsFromDisk(g.IO.IniFilename);
3543 g.SettingsLoaded = true;
3544 }
3545
3546 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3547 if (g.SettingsDirtyTimer > 0.0f)
3548 {
3549 g.SettingsDirtyTimer -= g.IO.DeltaTime;
3550 if (g.SettingsDirtyTimer <= 0.0f)
3551 {
3552 if (g.IO.IniFilename != NULL)
3553 SaveIniSettingsToDisk(g.IO.IniFilename);
3554 else
3555 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3556 g.SettingsDirtyTimer = 0.0f;
3557 }
3558 }
3559
3560 g.Time += g.IO.DeltaTime;
3561 g.WithinFrameScope = true;
3562 g.FrameCount += 1;
3563 g.TooltipOverrideCount = 0;
3564 g.WindowsActiveCount = 0;
3565
3566 // Setup current font and draw list shared data
3567 g.IO.Fonts->Locked = true;
3568 SetCurrentFont(GetDefaultFont());
3569 IM_ASSERT(g.Font->IsLoaded());
3570 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3571 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3572 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
3573 if (g.Style.AntiAliasedLines)
3574 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
3575 if (g.Style.AntiAliasedFill)
3576 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
3577 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
3578 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
3579
3580 g.BackgroundDrawList.Clear();
3581 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3582 g.BackgroundDrawList.PushClipRectFullScreen();
3583
3584 g.ForegroundDrawList.Clear();
3585 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID);
3586 g.ForegroundDrawList.PushClipRectFullScreen();
3587
3588 // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
3589 g.DrawData.Clear();
3590
3591 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3592 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3593 KeepAliveID(g.DragDropPayload.SourceId);
3594
3595 // Clear reference to active widget if the widget isn't alive anymore
3596 if (!g.HoveredIdPreviousFrame)
3597 g.HoveredIdTimer = 0.0f;
3598 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3599 g.HoveredIdNotActiveTimer = 0.0f;
3600 if (g.HoveredId)
3601 g.HoveredIdTimer += g.IO.DeltaTime;
3602 if (g.HoveredId && g.ActiveId != g.HoveredId)
3603 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3604 g.HoveredIdPreviousFrame = g.HoveredId;
3605 g.HoveredId = 0;
3606 g.HoveredIdAllowOverlap = false;
3607 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3608 ClearActiveID();
3609 if (g.ActiveId)
3610 g.ActiveIdTimer += g.IO.DeltaTime;
3611 g.LastActiveIdTimer += g.IO.DeltaTime;
3612 g.ActiveIdPreviousFrame = g.ActiveId;
3613 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3614 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
3615 g.ActiveIdIsAlive = 0;
3616 g.ActiveIdHasBeenEditedThisFrame = false;
3617 g.ActiveIdPreviousFrameIsAlive = false;
3618 g.ActiveIdIsJustActivated = false;
3619 if (g.TempInputTextId != 0 && g.ActiveId != g.TempInputTextId)
3620 g.TempInputTextId = 0;
3621 if (g.ActiveId == 0)
3622 {
3623 g.ActiveIdUsingNavDirMask = g.ActiveIdUsingNavInputMask = 0;
3624 g.ActiveIdUsingKeyInputMask = 0;
3625 }
3626
3627 // Drag and drop
3628 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3629 g.DragDropAcceptIdCurr = 0;
3630 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3631 g.DragDropWithinSourceOrTarget = false;
3632
3633 // Update keyboard input state
3634 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3635 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3636 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;
3637
3638 // Update gamepad/keyboard directional navigation
3639 NavUpdate();
3640
3641 // Update mouse input state
3642 UpdateMouseInputs();
3643
3644 // Calculate frame-rate for the user, as a purely luxurious feature
3645 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3646 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3647 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3648 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3649
3650 // Find hovered window
3651 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
3652 UpdateHoveredWindowAndCaptureFlags();
3653
3654 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3655 UpdateMouseMovingWindowNewFrame();
3656
3657 // Background darkening/whitening
3658 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3659 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3660 else
3661 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3662
3663 g.MouseCursor = ImGuiMouseCursor_Arrow;
3664 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3665 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3666
3667 // Mouse wheel scrolling, scale
3668 UpdateMouseWheel();
3669
3670 // Pressing TAB activate widget focus
3671 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab));
3672 if (g.ActiveId == 0 && g.FocusTabPressed)
3673 {
3674 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also
3675 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below.
3676 g.FocusRequestNextWindow = g.NavWindow;
3677 g.FocusRequestNextCounterAll = INT_MAX;
3678 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3679 g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3680 else
3681 g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0;
3682 }
3683
3684 // Turn queued focus request into current one
3685 g.FocusRequestCurrWindow = NULL;
3686 g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX;
3687 if (g.FocusRequestNextWindow != NULL)
3688 {
3689 ImGuiWindow* window = g.FocusRequestNextWindow;
3690 g.FocusRequestCurrWindow = window;
3691 if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1)
3692 g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1);
3693 if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1)
3694 g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1);
3695 g.FocusRequestNextWindow = NULL;
3696 g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX;
3697 }
3698
3699 g.NavIdTabCounter = INT_MAX;
3700
3701 // Mark all windows as not visible and compact unused memory.
3702 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3703 const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX;
3704 for (int i = 0; i != g.Windows.Size; i++)
3705 {
3706 ImGuiWindow* window = g.Windows[i];
3707 window->WasActive = window->Active;
3708 window->BeginCount = 0;
3709 window->Active = false;
3710 window->WriteAccessed = false;
3711
3712 // Garbage collect (this is totally functional but we may need decide if the side-effects are desirable)
3713 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
3714 GcCompactTransientWindowBuffers(window);
3715 }
3716
3717 // Closing the focused window restore focus to the first active root window in descending z-order
3718 if (g.NavWindow && !g.NavWindow->WasActive)
3719 FocusTopMostWindowUnderOne(NULL, NULL);
3720
3721 // No window should be open at the beginning of the frame.
3722 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3723 g.CurrentWindowStack.resize(0);
3724 g.BeginPopupStack.resize(0);
3725 ClosePopupsOverWindow(g.NavWindow, false);
3726
3727 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
3728 UpdateDebugToolItemPicker();
3729
3730 // Create implicit/fallback window - which we will only render it if the user has added something to it.
3731 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3732 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3733 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3734 Begin("Debug##Default");
3735 g.WithinFrameScopeWithImplicitWindow = true;
3736
3737 #ifdef IMGUI_ENABLE_TEST_ENGINE
3738 ImGuiTestEngineHook_PostNewFrame(&g);
3739 #endif
3740 }
3741
3742 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
UpdateDebugToolItemPicker()3743 void ImGui::UpdateDebugToolItemPicker()
3744 {
3745 ImGuiContext& g = *GImGui;
3746 g.DebugItemPickerBreakID = 0;
3747 if (g.DebugItemPickerActive)
3748 {
3749 const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
3750 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
3751 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
3752 g.DebugItemPickerActive = false;
3753 if (ImGui::IsMouseClicked(0) && hovered_id)
3754 {
3755 g.DebugItemPickerBreakID = hovered_id;
3756 g.DebugItemPickerActive = false;
3757 }
3758 ImGui::SetNextWindowBgAlpha(0.60f);
3759 ImGui::BeginTooltip();
3760 ImGui::Text("HoveredId: 0x%08X", hovered_id);
3761 ImGui::Text("Press ESC to abort picking.");
3762 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!");
3763 ImGui::EndTooltip();
3764 }
3765 }
3766
Initialize(ImGuiContext * context)3767 void ImGui::Initialize(ImGuiContext* context)
3768 {
3769 ImGuiContext& g = *context;
3770 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3771
3772 // Add .ini handle for ImGuiWindow type
3773 {
3774 ImGuiSettingsHandler ini_handler;
3775 ini_handler.TypeName = "Window";
3776 ini_handler.TypeHash = ImHashStr("Window");
3777 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
3778 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
3779 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
3780 g.SettingsHandlers.push_back(ini_handler);
3781 }
3782
3783 g.Initialized = true;
3784 }
3785
3786 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3787 void ImGui::Shutdown(ImGuiContext* context)
3788 {
3789 // 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)
3790 ImGuiContext& g = *context;
3791 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3792 {
3793 g.IO.Fonts->Locked = false;
3794 IM_DELETE(g.IO.Fonts);
3795 }
3796 g.IO.Fonts = NULL;
3797
3798 // Cleanup of other data are conditional on actually having initialized Dear ImGui.
3799 if (!g.Initialized)
3800 return;
3801
3802 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3803 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3804 {
3805 ImGuiContext* backup_context = GImGui;
3806 SetCurrentContext(context);
3807 SaveIniSettingsToDisk(g.IO.IniFilename);
3808 SetCurrentContext(backup_context);
3809 }
3810
3811 // Clear everything else
3812 for (int i = 0; i < g.Windows.Size; i++)
3813 IM_DELETE(g.Windows[i]);
3814 g.Windows.clear();
3815 g.WindowsFocusOrder.clear();
3816 g.WindowsSortBuffer.clear();
3817 g.CurrentWindow = NULL;
3818 g.CurrentWindowStack.clear();
3819 g.WindowsById.Clear();
3820 g.NavWindow = NULL;
3821 g.HoveredWindow = g.HoveredRootWindow = NULL;
3822 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3823 g.MovingWindow = NULL;
3824 g.ColorModifiers.clear();
3825 g.StyleModifiers.clear();
3826 g.FontStack.clear();
3827 g.OpenPopupStack.clear();
3828 g.BeginPopupStack.clear();
3829 g.DrawDataBuilder.ClearFreeMemory();
3830 g.BackgroundDrawList.ClearFreeMemory();
3831 g.ForegroundDrawList.ClearFreeMemory();
3832
3833 g.TabBars.Clear();
3834 g.CurrentTabBarStack.clear();
3835 g.ShrinkWidthBuffer.clear();
3836
3837 g.PrivateClipboard.clear();
3838 g.InputTextState.ClearFreeMemory();
3839
3840 g.SettingsWindows.clear();
3841 g.SettingsHandlers.clear();
3842
3843 if (g.LogFile)
3844 {
3845 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
3846 if (g.LogFile != stdout)
3847 #endif
3848 ImFileClose(g.LogFile);
3849 g.LogFile = NULL;
3850 }
3851 g.LogBuffer.clear();
3852
3853 g.Initialized = false;
3854 }
3855
3856 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3857 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3858 {
3859 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3860 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3861 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3862 return d;
3863 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3864 return d;
3865 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3866 }
3867
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3868 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3869 {
3870 out_sorted_windows->push_back(window);
3871 if (window->Active)
3872 {
3873 int count = window->DC.ChildWindows.Size;
3874 if (count > 1)
3875 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3876 for (int i = 0; i < count; i++)
3877 {
3878 ImGuiWindow* child = window->DC.ChildWindows[i];
3879 if (child->Active)
3880 AddWindowToSortBuffer(out_sorted_windows, child);
3881 }
3882 }
3883 }
3884
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3885 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3886 {
3887 if (draw_list->CmdBuffer.empty())
3888 return;
3889
3890 // Remove trailing command if unused
3891 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3892 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3893 {
3894 draw_list->CmdBuffer.pop_back();
3895 if (draw_list->CmdBuffer.empty())
3896 return;
3897 }
3898
3899 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
3900 // May trigger for you if you are using PrimXXX functions incorrectly.
3901 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3902 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3903 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
3904 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3905
3906 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3907 // If this assert triggers because you are drawing lots of stuff manually:
3908 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
3909 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents.
3910 // - If you want large meshes with more than 64K vertices, you can either:
3911 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
3912 // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't.
3913 // 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.
3914 // (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
3915 // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time:
3916 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3917 // Your own engine or render API may use different parameters or function calls to specify index sizes.
3918 // 2 and 4 bytes indices are generally supported by most graphics API.
3919 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
3920 // the 64K limit to split your draw commands in multiple draw lists.
3921 if (sizeof(ImDrawIdx) == 2)
3922 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3923
3924 out_list->push_back(draw_list);
3925 }
3926
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3927 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3928 {
3929 ImGuiContext& g = *GImGui;
3930 g.IO.MetricsRenderWindows++;
3931 AddDrawListToDrawData(out_render_list, window->DrawList);
3932 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3933 {
3934 ImGuiWindow* child = window->DC.ChildWindows[i];
3935 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3936 AddWindowToDrawData(out_render_list, child);
3937 }
3938 }
3939
3940 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
AddRootWindowToDrawData(ImGuiWindow * window)3941 static void AddRootWindowToDrawData(ImGuiWindow* window)
3942 {
3943 ImGuiContext& g = *GImGui;
3944 if (window->Flags & ImGuiWindowFlags_Tooltip)
3945 AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3946 else
3947 AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3948 }
3949
FlattenIntoSingleLayer()3950 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3951 {
3952 int n = Layers[0].Size;
3953 int size = n;
3954 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3955 size += Layers[i].Size;
3956 Layers[0].resize(size);
3957 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3958 {
3959 ImVector<ImDrawList*>& layer = Layers[layer_n];
3960 if (layer.empty())
3961 continue;
3962 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3963 n += layer.Size;
3964 layer.resize(0);
3965 }
3966 }
3967
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3968 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3969 {
3970 ImGuiIO& io = ImGui::GetIO();
3971 draw_data->Valid = true;
3972 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3973 draw_data->CmdListsCount = draw_lists->Size;
3974 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3975 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3976 draw_data->DisplaySize = io.DisplaySize;
3977 draw_data->FramebufferScale = io.DisplayFramebufferScale;
3978 for (int n = 0; n < draw_lists->Size; n++)
3979 {
3980 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3981 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3982 }
3983 }
3984
3985 // 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)3986 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3987 {
3988 ImGuiWindow* window = GetCurrentWindow();
3989 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3990 window->ClipRect = window->DrawList->_ClipRectStack.back();
3991 }
3992
PopClipRect()3993 void ImGui::PopClipRect()
3994 {
3995 ImGuiWindow* window = GetCurrentWindow();
3996 window->DrawList->PopClipRect();
3997 window->ClipRect = window->DrawList->_ClipRectStack.back();
3998 }
3999
4000 // 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()4001 void ImGui::EndFrame()
4002 {
4003 ImGuiContext& g = *GImGui;
4004 IM_ASSERT(g.Initialized);
4005 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
4006 return;
4007 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?");
4008
4009 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
4010 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f))
4011 {
4012 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
4013 g.PlatformImeLastPos = g.PlatformImePos;
4014 }
4015
4016 ErrorCheckEndFrame();
4017
4018 // Hide implicit/fallback "Debug" window if it hasn't been used
4019 g.WithinFrameScopeWithImplicitWindow = false;
4020 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
4021 g.CurrentWindow->Active = false;
4022 End();
4023
4024 // Show CTRL+TAB list window
4025 if (g.NavWindowingTarget != NULL)
4026 NavUpdateWindowingOverlay();
4027
4028 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
4029 if (g.DragDropActive)
4030 {
4031 bool is_delivered = g.DragDropPayload.Delivery;
4032 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
4033 if (is_delivered || is_elapsed)
4034 ClearDragDrop();
4035 }
4036
4037 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
4038 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
4039 {
4040 g.DragDropWithinSourceOrTarget = true;
4041 SetTooltip("...");
4042 g.DragDropWithinSourceOrTarget = false;
4043 }
4044
4045 // End frame
4046 g.WithinFrameScope = false;
4047 g.FrameCountEnded = g.FrameCount;
4048
4049 // Initiate moving window + handle left-click and right-click focus
4050 UpdateMouseMovingWindowEndFrame();
4051
4052 // Sort the window list so that all child windows are after their parent
4053 // We cannot do that on FocusWindow() because childs may not exist yet
4054 g.WindowsSortBuffer.resize(0);
4055 g.WindowsSortBuffer.reserve(g.Windows.Size);
4056 for (int i = 0; i != g.Windows.Size; i++)
4057 {
4058 ImGuiWindow* window = g.Windows[i];
4059 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
4060 continue;
4061 AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
4062 }
4063
4064 // 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.
4065 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size);
4066 g.Windows.swap(g.WindowsSortBuffer);
4067 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
4068
4069 // Unlock font atlas
4070 g.IO.Fonts->Locked = false;
4071
4072 // Clear Input data for next frame
4073 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
4074 g.IO.InputQueueCharacters.resize(0);
4075 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
4076 }
4077
Render()4078 void ImGui::Render()
4079 {
4080 ImGuiContext& g = *GImGui;
4081 IM_ASSERT(g.Initialized);
4082
4083 if (g.FrameCountEnded != g.FrameCount)
4084 EndFrame();
4085 g.FrameCountRendered = g.FrameCount;
4086
4087 // Gather ImDrawList to render (for each active window)
4088 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
4089 g.DrawDataBuilder.Clear();
4090 if (!g.BackgroundDrawList.VtxBuffer.empty())
4091 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList);
4092
4093 ImGuiWindow* windows_to_render_top_most[2];
4094 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
4095 windows_to_render_top_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
4096 for (int n = 0; n != g.Windows.Size; n++)
4097 {
4098 ImGuiWindow* window = g.Windows[n];
4099 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
4100 AddRootWindowToDrawData(window);
4101 }
4102 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
4103 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
4104 AddRootWindowToDrawData(windows_to_render_top_most[n]);
4105 g.DrawDataBuilder.FlattenIntoSingleLayer();
4106
4107 // Draw software mouse cursor if requested
4108 if (g.IO.MouseDrawCursor)
4109 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
4110
4111 if (!g.ForegroundDrawList.VtxBuffer.empty())
4112 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList);
4113
4114 // Setup ImDrawData structure for end-user
4115 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
4116 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
4117 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
4118
4119 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
4120 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4121 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
4122 g.IO.RenderDrawListsFn(&g.DrawData);
4123 #endif
4124 }
4125
4126 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
4127 // 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)4128 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
4129 {
4130 ImGuiContext& g = *GImGui;
4131
4132 const char* text_display_end;
4133 if (hide_text_after_double_hash)
4134 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
4135 else
4136 text_display_end = text_end;
4137
4138 ImFont* font = g.Font;
4139 const float font_size = g.FontSize;
4140 if (text == text_display_end)
4141 return ImVec2(0.0f, font_size);
4142 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
4143
4144 // Round
4145 // XXX(sturnclaw): ported from ocornut/imgui 4622f14b
4146 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
4147 // FIXME: Investigate using ceilf or e.g.
4148 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
4149 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
4150 text_size.x = IM_FLOOR(text_size.x + 0.99999f);
4151
4152 return text_size;
4153 }
4154
4155 // Find window given position, search front-to-back
4156 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
4157 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
4158 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()4159 static void FindHoveredWindow()
4160 {
4161 ImGuiContext& g = *GImGui;
4162
4163 ImGuiWindow* hovered_window = NULL;
4164 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
4165 hovered_window = g.MovingWindow;
4166
4167 ImVec2 padding_regular = g.Style.TouchExtraPadding;
4168 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;
4169 for (int i = g.Windows.Size - 1; i >= 0; i--)
4170 {
4171 ImGuiWindow* window = g.Windows[i];
4172 if (!window->Active || window->Hidden)
4173 continue;
4174 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
4175 continue;
4176
4177 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
4178 ImRect bb(window->OuterRectClipped);
4179 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
4180 bb.Expand(padding_regular);
4181 else
4182 bb.Expand(padding_for_resize_from_edges);
4183 if (!bb.Contains(g.IO.MousePos))
4184 continue;
4185
4186 // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
4187 if (hovered_window == NULL)
4188 hovered_window = window;
4189 if (hovered_window)
4190 break;
4191 }
4192
4193 g.HoveredWindow = hovered_window;
4194 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
4195
4196 }
4197
4198 // Test if mouse cursor is hovering given rectangle
4199 // NB- Rectangle is clipped by our current clip setting
4200 // 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)4201 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
4202 {
4203 ImGuiContext& g = *GImGui;
4204
4205 // Clip
4206 ImRect rect_clipped(r_min, r_max);
4207 if (clip)
4208 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
4209
4210 // Expand for touch input
4211 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
4212 if (!rect_for_touch.Contains(g.IO.MousePos))
4213 return false;
4214 return true;
4215 }
4216
GetKeyIndex(ImGuiKey imgui_key)4217 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
4218 {
4219 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
4220 ImGuiContext& g = *GImGui;
4221 return g.IO.KeyMap[imgui_key];
4222 }
4223
4224 // Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]!
IsKeyDown(int user_key_index)4225 bool ImGui::IsKeyDown(int user_key_index)
4226 {
4227 if (user_key_index < 0)
4228 return false;
4229 ImGuiContext& g = *GImGui;
4230 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4231 return g.IO.KeysDown[user_key_index];
4232 }
4233
4234 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
4235 // t1 = current time (e.g.: g.Time)
4236 // An event is triggered at:
4237 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N
CalcTypematicRepeatAmount(float t0,float t1,float repeat_delay,float repeat_rate)4238 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
4239 {
4240 if (t1 == 0.0f)
4241 return 1;
4242 if (t0 >= t1)
4243 return 0;
4244 if (repeat_rate <= 0.0f)
4245 return (t0 < repeat_delay) && (t1 >= repeat_delay);
4246 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
4247 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
4248 const int count = count_t1 - count_t0;
4249 return count;
4250 }
4251
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)4252 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
4253 {
4254 ImGuiContext& g = *GImGui;
4255 if (key_index < 0)
4256 return 0;
4257 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4258 const float t = g.IO.KeysDownDuration[key_index];
4259 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
4260 }
4261
IsKeyPressed(int user_key_index,bool repeat)4262 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
4263 {
4264 ImGuiContext& g = *GImGui;
4265 if (user_key_index < 0)
4266 return false;
4267 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4268 const float t = g.IO.KeysDownDuration[user_key_index];
4269 if (t == 0.0f)
4270 return true;
4271 if (repeat && t > g.IO.KeyRepeatDelay)
4272 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4273 return false;
4274 }
4275
IsKeyReleased(int user_key_index)4276 bool ImGui::IsKeyReleased(int user_key_index)
4277 {
4278 ImGuiContext& g = *GImGui;
4279 if (user_key_index < 0) return false;
4280 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4281 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4282 }
4283
IsMouseDown(int button)4284 bool ImGui::IsMouseDown(int button)
4285 {
4286 ImGuiContext& g = *GImGui;
4287 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4288 return g.IO.MouseDown[button];
4289 }
4290
IsAnyMouseDown()4291 bool ImGui::IsAnyMouseDown()
4292 {
4293 ImGuiContext& g = *GImGui;
4294 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4295 if (g.IO.MouseDown[n])
4296 return true;
4297 return false;
4298 }
4299
IsMouseClicked(int button,bool repeat)4300 bool ImGui::IsMouseClicked(int button, bool repeat)
4301 {
4302 ImGuiContext& g = *GImGui;
4303 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4304 const float t = g.IO.MouseDownDuration[button];
4305 if (t == 0.0f)
4306 return true;
4307
4308 if (repeat && t > g.IO.KeyRepeatDelay)
4309 {
4310 // 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.
4311 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f);
4312 if (amount > 0)
4313 return true;
4314 }
4315
4316 return false;
4317 }
4318
IsMouseReleased(int button)4319 bool ImGui::IsMouseReleased(int button)
4320 {
4321 ImGuiContext& g = *GImGui;
4322 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4323 return g.IO.MouseReleased[button];
4324 }
4325
IsMouseDoubleClicked(int button)4326 bool ImGui::IsMouseDoubleClicked(int button)
4327 {
4328 ImGuiContext& g = *GImGui;
4329 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4330 return g.IO.MouseDoubleClicked[button];
4331 }
4332
4333 // [Internal] This doesn't test if the button is pressed
IsMouseDragPastThreshold(int button,float lock_threshold)4334 bool ImGui::IsMouseDragPastThreshold(int button, float lock_threshold)
4335 {
4336 ImGuiContext& g = *GImGui;
4337 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4338 if (lock_threshold < 0.0f)
4339 lock_threshold = g.IO.MouseDragThreshold;
4340 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4341 }
4342
IsMouseDragging(int button,float lock_threshold)4343 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4344 {
4345 ImGuiContext& g = *GImGui;
4346 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4347 if (!g.IO.MouseDown[button])
4348 return false;
4349 return IsMouseDragPastThreshold(button, lock_threshold);
4350 }
4351
GetMousePos()4352 ImVec2 ImGui::GetMousePos()
4353 {
4354 return GImGui->IO.MousePos;
4355 }
4356
4357 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4358 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4359 {
4360 ImGuiContext& g = *GImGui;
4361 if (g.BeginPopupStack.Size > 0)
4362 return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4363 return g.IO.MousePos;
4364 }
4365
4366 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
IsMousePosValid(const ImVec2 * mouse_pos)4367 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4368 {
4369 // The assert is only to silence a false-positive in XCode Static Analysis.
4370 // 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).
4371 IM_ASSERT(GImGui != NULL);
4372 const float MOUSE_INVALID = -256000.0f;
4373 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
4374 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
4375 }
4376
4377 // Return the delta from the initial clicking position while the mouse button is clicked or was just released.
4378 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4379 // 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(int button,float lock_threshold)4380 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4381 {
4382 ImGuiContext& g = *GImGui;
4383 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4384 if (lock_threshold < 0.0f)
4385 lock_threshold = g.IO.MouseDragThreshold;
4386 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
4387 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4388 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
4389 return g.IO.MousePos - g.IO.MouseClickedPos[button];
4390 return ImVec2(0.0f, 0.0f);
4391 }
4392
ResetMouseDragDelta(int button)4393 void ImGui::ResetMouseDragDelta(int button)
4394 {
4395 ImGuiContext& g = *GImGui;
4396 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4397 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4398 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4399 }
4400
GetMouseCursor()4401 ImGuiMouseCursor ImGui::GetMouseCursor()
4402 {
4403 return GImGui->MouseCursor;
4404 }
4405
SetMouseCursor(ImGuiMouseCursor cursor_type)4406 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4407 {
4408 GImGui->MouseCursor = cursor_type;
4409 }
4410
CaptureKeyboardFromApp(bool capture)4411 void ImGui::CaptureKeyboardFromApp(bool capture)
4412 {
4413 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4414 }
4415
CaptureMouseFromApp(bool capture)4416 void ImGui::CaptureMouseFromApp(bool capture)
4417 {
4418 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4419 }
4420
IsItemActive()4421 bool ImGui::IsItemActive()
4422 {
4423 ImGuiContext& g = *GImGui;
4424 if (g.ActiveId)
4425 {
4426 ImGuiWindow* window = g.CurrentWindow;
4427 return g.ActiveId == window->DC.LastItemId;
4428 }
4429 return false;
4430 }
4431
IsItemActivated()4432 bool ImGui::IsItemActivated()
4433 {
4434 ImGuiContext& g = *GImGui;
4435 if (g.ActiveId)
4436 {
4437 ImGuiWindow* window = g.CurrentWindow;
4438 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId)
4439 return true;
4440 }
4441 return false;
4442 }
4443
IsItemDeactivated()4444 bool ImGui::IsItemDeactivated()
4445 {
4446 ImGuiContext& g = *GImGui;
4447 ImGuiWindow* window = g.CurrentWindow;
4448 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated)
4449 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
4450 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4451 }
4452
IsItemDeactivatedAfterEdit()4453 bool ImGui::IsItemDeactivatedAfterEdit()
4454 {
4455 ImGuiContext& g = *GImGui;
4456 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));
4457 }
4458
IsItemFocused()4459 bool ImGui::IsItemFocused()
4460 {
4461 ImGuiContext& g = *GImGui;
4462 ImGuiWindow* window = g.CurrentWindow;
4463
4464 if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
4465 return false;
4466 return true;
4467 }
4468
IsItemClicked(int mouse_button)4469 bool ImGui::IsItemClicked(int mouse_button)
4470 {
4471 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4472 }
4473
IsItemToggledOpen()4474 bool ImGui::IsItemToggledOpen()
4475 {
4476 ImGuiContext& g = *GImGui;
4477 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
4478 }
4479
IsItemToggledSelection()4480 bool ImGui::IsItemToggledSelection()
4481 {
4482 ImGuiContext& g = *GImGui;
4483 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
4484 }
4485
IsAnyItemHovered()4486 bool ImGui::IsAnyItemHovered()
4487 {
4488 ImGuiContext& g = *GImGui;
4489 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4490 }
4491
IsAnyItemActive()4492 bool ImGui::IsAnyItemActive()
4493 {
4494 ImGuiContext& g = *GImGui;
4495 return g.ActiveId != 0;
4496 }
4497
IsAnyItemFocused()4498 bool ImGui::IsAnyItemFocused()
4499 {
4500 ImGuiContext& g = *GImGui;
4501 return g.NavId != 0 && !g.NavDisableHighlight;
4502 }
4503
IsItemVisible()4504 bool ImGui::IsItemVisible()
4505 {
4506 ImGuiWindow* window = GetCurrentWindowRead();
4507 return window->ClipRect.Overlaps(window->DC.LastItemRect);
4508 }
4509
IsItemEdited()4510 bool ImGui::IsItemEdited()
4511 {
4512 ImGuiWindow* window = GetCurrentWindowRead();
4513 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4514 }
4515
4516 // 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()4517 void ImGui::SetItemAllowOverlap()
4518 {
4519 ImGuiContext& g = *GImGui;
4520 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4521 g.HoveredIdAllowOverlap = true;
4522 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4523 g.ActiveIdAllowOverlap = true;
4524 }
4525
GetItemRectMin()4526 ImVec2 ImGui::GetItemRectMin()
4527 {
4528 ImGuiWindow* window = GetCurrentWindowRead();
4529 return window->DC.LastItemRect.Min;
4530 }
4531
GetItemRectMax()4532 ImVec2 ImGui::GetItemRectMax()
4533 {
4534 ImGuiWindow* window = GetCurrentWindowRead();
4535 return window->DC.LastItemRect.Max;
4536 }
4537
GetItemRectSize()4538 ImVec2 ImGui::GetItemRectSize()
4539 {
4540 ImGuiWindow* window = GetCurrentWindowRead();
4541 return window->DC.LastItemRect.GetSize();
4542 }
4543
GetViewportRect()4544 static ImRect GetViewportRect()
4545 {
4546 ImGuiContext& g = *GImGui;
4547 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4548 }
4549
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4550 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4551 {
4552 ImGuiContext& g = *GImGui;
4553 ImGuiWindow* parent_window = g.CurrentWindow;
4554
4555 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4556 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4557
4558 // Size
4559 const ImVec2 content_avail = GetContentRegionAvail();
4560 ImVec2 size = ImFloor(size_arg);
4561 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4562 if (size.x <= 0.0f)
4563 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4564 if (size.y <= 0.0f)
4565 size.y = ImMax(content_avail.y + size.y, 4.0f);
4566 SetNextWindowSize(size);
4567
4568 // 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.
4569 char title[256];
4570 if (name)
4571 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4572 else
4573 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4574
4575 const float backup_border_size = g.Style.ChildBorderSize;
4576 if (!border)
4577 g.Style.ChildBorderSize = 0.0f;
4578 bool ret = Begin(title, NULL, flags);
4579 g.Style.ChildBorderSize = backup_border_size;
4580
4581 ImGuiWindow* child_window = g.CurrentWindow;
4582 child_window->ChildId = id;
4583 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises;
4584
4585 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4586 // While this is not really documented/defined, it seems that the expected thing to do.
4587 if (child_window->BeginCount == 1)
4588 parent_window->DC.CursorPos = child_window->Pos;
4589
4590 // Process navigation-in immediately so NavInit can run on first frame
4591 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4592 {
4593 FocusWindow(child_window);
4594 NavInitWindow(child_window, false);
4595 SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4596 g.ActiveIdSource = ImGuiInputSource_Nav;
4597 }
4598 return ret;
4599 }
4600
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4601 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4602 {
4603 ImGuiWindow* window = GetCurrentWindow();
4604 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4605 }
4606
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4607 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4608 {
4609 IM_ASSERT(id != 0);
4610 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4611 }
4612
EndChild()4613 void ImGui::EndChild()
4614 {
4615 ImGuiContext& g = *GImGui;
4616 ImGuiWindow* window = g.CurrentWindow;
4617
4618 IM_ASSERT(g.WithinEndChild == false);
4619 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
4620
4621 g.WithinEndChild = true;
4622 if (window->BeginCount > 1)
4623 {
4624 End();
4625 }
4626 else
4627 {
4628 ImVec2 sz = window->Size;
4629 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4630 sz.x = ImMax(4.0f, sz.x);
4631 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4632 sz.y = ImMax(4.0f, sz.y);
4633 End();
4634
4635 ImGuiWindow* parent_window = g.CurrentWindow;
4636 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4637 ItemSize(sz);
4638 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4639 {
4640 ItemAdd(bb, window->ChildId);
4641 RenderNavHighlight(bb, window->ChildId);
4642
4643 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4644 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4645 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4646 }
4647 else
4648 {
4649 // Not navigable into
4650 ItemAdd(bb, 0);
4651 }
4652 }
4653 g.WithinEndChild = false;
4654 }
4655
4656 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4657 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4658 {
4659 ImGuiContext& g = *GImGui;
4660 const ImGuiStyle& style = g.Style;
4661 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4662 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4663 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4664 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4665 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4666 PopStyleVar(3);
4667 PopStyleColor();
4668 return ret;
4669 }
4670
EndChildFrame()4671 void ImGui::EndChildFrame()
4672 {
4673 EndChild();
4674 }
4675
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4676 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4677 {
4678 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4679 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4680 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4681 }
4682
FindWindowByID(ImGuiID id)4683 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4684 {
4685 ImGuiContext& g = *GImGui;
4686 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4687 }
4688
FindWindowByName(const char * name)4689 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4690 {
4691 ImGuiID id = ImHashStr(name);
4692 return FindWindowByID(id);
4693 }
4694
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4695 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4696 {
4697 ImGuiContext& g = *GImGui;
4698 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
4699
4700 // Create window the first time
4701 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4702 window->Flags = flags;
4703 g.WindowsById.SetVoidPtr(window->ID, window);
4704
4705 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4706 window->Pos = ImVec2(60, 60);
4707
4708 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4709 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4710 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4711 {
4712 // Retrieve settings from .ini file
4713 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
4714 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4715 window->Pos = ImVec2(settings->Pos.x, settings->Pos.y);
4716 window->Collapsed = settings->Collapsed;
4717 if (settings->Size.x > 0 && settings->Size.y > 0)
4718 size = ImVec2(settings->Size.x, settings->Size.y);
4719 }
4720 window->Size = window->SizeFull = ImFloor(size);
4721 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values
4722
4723 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4724 {
4725 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4726 window->AutoFitOnlyGrows = false;
4727 }
4728 else
4729 {
4730 if (window->Size.x <= 0.0f)
4731 window->AutoFitFramesX = 2;
4732 if (window->Size.y <= 0.0f)
4733 window->AutoFitFramesY = 2;
4734 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4735 }
4736
4737 g.WindowsFocusOrder.push_back(window);
4738 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4739 g.Windows.push_front(window); // Quite slow but rare and only once
4740 else
4741 g.Windows.push_back(window);
4742 return window;
4743 }
4744
CalcWindowSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4745 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4746 {
4747 ImGuiContext& g = *GImGui;
4748 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
4749 {
4750 // Using -1,-1 on either X/Y axis to preserve the current size.
4751 ImRect cr = g.NextWindowData.SizeConstraintRect;
4752 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4753 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4754 if (g.NextWindowData.SizeCallback)
4755 {
4756 ImGuiSizeCallbackData data;
4757 data.UserData = g.NextWindowData.SizeCallbackUserData;
4758 data.Pos = window->Pos;
4759 data.CurrentSize = window->SizeFull;
4760 data.DesiredSize = new_size;
4761 g.NextWindowData.SizeCallback(&data);
4762 new_size = data.DesiredSize;
4763 }
4764 new_size.x = IM_FLOOR(new_size.x);
4765 new_size.y = IM_FLOOR(new_size.y);
4766 }
4767
4768 // Minimum size
4769 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4770 {
4771 new_size = ImMax(new_size, g.Style.WindowMinSize);
4772 new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
4773 }
4774 return new_size;
4775 }
4776
CalcWindowContentSize(ImGuiWindow * window)4777 static ImVec2 CalcWindowContentSize(ImGuiWindow* window)
4778 {
4779 if (window->Collapsed)
4780 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
4781 return window->ContentSize;
4782 if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
4783 return window->ContentSize;
4784
4785 ImVec2 sz;
4786 sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
4787 sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
4788 return sz;
4789 }
4790
CalcWindowAutoFitSize(ImGuiWindow * window,const ImVec2 & size_contents)4791 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
4792 {
4793 ImGuiContext& g = *GImGui;
4794 ImGuiStyle& style = g.Style;
4795 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight());
4796 ImVec2 size_pad = window->WindowPadding * 2.0f;
4797 ImVec2 size_desired = size_contents + size_pad + size_decorations;
4798 if (window->Flags & ImGuiWindowFlags_Tooltip)
4799 {
4800 // Tooltip always resize
4801 return size_desired;
4802 }
4803 else
4804 {
4805 // Maximum window size is determined by the viewport size or monitor size
4806 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4807 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4808 ImVec2 size_min = style.WindowMinSize;
4809 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)
4810 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4811 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4812
4813 // When the window cannot fit all contents (either because of constraints, either because screen is too small),
4814 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
4815 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4816 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);
4817 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);
4818 if (will_have_scrollbar_x)
4819 size_auto_fit.y += style.ScrollbarSize;
4820 if (will_have_scrollbar_y)
4821 size_auto_fit.x += style.ScrollbarSize;
4822 return size_auto_fit;
4823 }
4824 }
4825
CalcWindowExpectedSize(ImGuiWindow * window)4826 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4827 {
4828 ImVec2 size_contents = CalcWindowContentSize(window);
4829 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents);
4830 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4831 return size_final;
4832 }
4833
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4834 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4835 {
4836 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4837 return ImGuiCol_PopupBg;
4838 if (flags & ImGuiWindowFlags_ChildWindow)
4839 return ImGuiCol_ChildBg;
4840 return ImGuiCol_WindowBg;
4841 }
4842
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4843 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4844 {
4845 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
4846 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4847 ImVec2 size_expected = pos_max - pos_min;
4848 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
4849 *out_pos = pos_min;
4850 if (corner_norm.x == 0.0f)
4851 out_pos->x -= (size_constrained.x - size_expected.x);
4852 if (corner_norm.y == 0.0f)
4853 out_pos->y -= (size_constrained.y - size_expected.y);
4854 *out_size = size_constrained;
4855 }
4856
4857 struct ImGuiResizeGripDef
4858 {
4859 ImVec2 CornerPosN;
4860 ImVec2 InnerDir;
4861 int AngleMin12, AngleMax12;
4862 };
4863
4864 static const ImGuiResizeGripDef resize_grip_def[4] =
4865 {
4866 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower-right
4867 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower-left
4868 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper-left (Unused)
4869 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper-right (Unused)
4870 };
4871
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4872 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4873 {
4874 ImRect rect = window->Rect();
4875 if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4876 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
4877 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
4878 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
4879 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
4880 IM_ASSERT(0);
4881 return ImRect();
4882 }
4883
4884 // 0..3: corners (Lower-right, Lower-left, Unused, Unused)
4885 // 4..7: borders (Top, Right, Bottom, Left)
GetWindowResizeID(ImGuiWindow * window,int n)4886 ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n)
4887 {
4888 IM_ASSERT(n >= 0 && n <= 7);
4889 ImGuiID id = window->ID;
4890 id = ImHashStr("#RESIZE", 0, id);
4891 id = ImHashData(&n, sizeof(int), id);
4892 return id;
4893 }
4894
4895 // Handle resize for: Resize Grips, Borders, Gamepad
4896 // 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])4897 static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4898 {
4899 ImGuiContext& g = *GImGui;
4900 ImGuiWindowFlags flags = window->Flags;
4901
4902 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4903 return false;
4904 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4905 return false;
4906
4907 bool ret_auto_fit = false;
4908 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4909 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
4910 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f);
4911 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4912
4913 ImVec2 pos_target(FLT_MAX, FLT_MAX);
4914 ImVec2 size_target(FLT_MAX, FLT_MAX);
4915
4916 // Resize grips and borders are on layer 1
4917 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
4918 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
4919
4920 // Manual resize grips
4921 PushID("#RESIZE");
4922 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4923 {
4924 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4925 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
4926
4927 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4928 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4929 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4930 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4931 bool hovered, held;
4932 ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4933 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4934 if (hovered || held)
4935 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4936
4937 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4938 {
4939 // Manual auto-fit when double-clicking
4940 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
4941 ret_auto_fit = true;
4942 ClearActiveID();
4943 }
4944 else if (held)
4945 {
4946 // Resize from any of the four corners
4947 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4948 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
4949 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target);
4950 }
4951 if (resize_grip_n == 0 || held || hovered)
4952 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4953 }
4954 for (int border_n = 0; border_n < resize_border_count; border_n++)
4955 {
4956 bool hovered, held;
4957 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4958 ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4959 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4960 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4961 {
4962 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4963 if (held)
4964 *border_held = border_n;
4965 }
4966 if (held)
4967 {
4968 ImVec2 border_target = window->Pos;
4969 ImVec2 border_posn;
4970 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
4971 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
4972 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
4973 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
4974 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4975 }
4976 }
4977 PopID();
4978
4979 // Navigation resize (keyboard/gamepad)
4980 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4981 {
4982 ImVec2 nav_resize_delta;
4983 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4984 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4985 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4986 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4987 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4988 {
4989 const float NAV_RESIZE_SPEED = 600.0f;
4990 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4991 g.NavWindowingToggleLayer = false;
4992 g.NavDisableMouseHover = true;
4993 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4994 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4995 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4996 }
4997 }
4998
4999 // Apply back modified position/size to window
5000 if (size_target.x != FLT_MAX)
5001 {
5002 window->SizeFull = size_target;
5003 MarkIniSettingsDirty(window);
5004 }
5005 if (pos_target.x != FLT_MAX)
5006 {
5007 window->Pos = ImFloor(pos_target);
5008 MarkIniSettingsDirty(window);
5009 }
5010
5011 // Resize nav layer
5012 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5013 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5014
5015 window->Size = window->SizeFull;
5016 return ret_auto_fit;
5017 }
5018
ClampWindowRect(ImGuiWindow * window,const ImRect & rect,const ImVec2 & padding)5019 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding)
5020 {
5021 ImGuiContext& g = *GImGui;
5022 ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5023 window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping);
5024 }
5025
RenderWindowOuterBorders(ImGuiWindow * window)5026 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
5027 {
5028 ImGuiContext& g = *GImGui;
5029 float rounding = window->WindowRounding;
5030 float border_size = window->WindowBorderSize;
5031 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))
5032 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
5033
5034 int border_held = window->ResizeBorderHeld;
5035 if (border_held != -1)
5036 {
5037 struct ImGuiResizeBorderDef
5038 {
5039 ImVec2 InnerDir;
5040 ImVec2 CornerPosN1, CornerPosN2;
5041 float OuterAngle;
5042 };
5043 static const ImGuiResizeBorderDef resize_border_def[4] =
5044 {
5045 { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top
5046 { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right
5047 { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom
5048 { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left
5049 };
5050 const ImGuiResizeBorderDef& def = resize_border_def[border_held];
5051 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f);
5052 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);
5053 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);
5054 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual
5055 }
5056 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
5057 {
5058 float y = window->Pos.y + window->TitleBarHeight() - 1;
5059 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);
5060 }
5061 }
5062
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)5063 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)
5064 {
5065 ImGuiContext& g = *GImGui;
5066 ImGuiStyle& style = g.Style;
5067 ImGuiWindowFlags flags = window->Flags;
5068
5069 // Ensure that ScrollBar doesn't read last frame's SkipItems
5070 window->SkipItems = false;
5071
5072 // Draw window + handle manual resize
5073 // 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.
5074 const float window_rounding = window->WindowRounding;
5075 const float window_border_size = window->WindowBorderSize;
5076 if (window->Collapsed)
5077 {
5078 // Title bar only
5079 float backup_border_size = style.FrameBorderSize;
5080 g.Style.FrameBorderSize = window->WindowBorderSize;
5081 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5082 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5083 g.Style.FrameBorderSize = backup_border_size;
5084 }
5085 else
5086 {
5087 // Window background
5088 if (!(flags & ImGuiWindowFlags_NoBackground))
5089 {
5090 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5091 float alpha = 1.0f;
5092 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)
5093 alpha = g.NextWindowData.BgAlphaVal;
5094 if (alpha != 1.0f)
5095 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
5096 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5097 }
5098
5099 // Title bar
5100 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5101 {
5102 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5103 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5104 }
5105
5106 // Menu bar
5107 if (flags & ImGuiWindowFlags_MenuBar)
5108 {
5109 ImRect menu_bar_rect = window->MenuBarRect();
5110 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.
5111 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);
5112 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5113 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5114 }
5115
5116 // Scrollbars
5117 if (window->ScrollbarX)
5118 Scrollbar(ImGuiAxis_X);
5119 if (window->ScrollbarY)
5120 Scrollbar(ImGuiAxis_Y);
5121
5122 // Render resize grips (after their input handling so we don't have a frame of latency)
5123 if (!(flags & ImGuiWindowFlags_NoResize))
5124 {
5125 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5126 {
5127 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5128 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
5129 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)));
5130 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)));
5131 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);
5132 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5133 }
5134 }
5135
5136 // Borders
5137 RenderWindowOuterBorders(window);
5138 }
5139 }
5140
5141 // Render title text, collapse button, close button
RenderWindowTitleBarContents(ImGuiWindow * window,const ImRect & title_bar_rect,const char * name,bool * p_open)5142 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
5143 {
5144 ImGuiContext& g = *GImGui;
5145 ImGuiStyle& style = g.Style;
5146 ImGuiWindowFlags flags = window->Flags;
5147
5148 const bool has_close_button = (p_open != NULL);
5149 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
5150
5151 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
5152 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5153 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5154 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5155 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5156
5157 // Layout buttons
5158 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
5159 float pad_l = style.FramePadding.x;
5160 float pad_r = style.FramePadding.x;
5161 float button_sz = g.FontSize;
5162 ImVec2 close_button_pos;
5163 ImVec2 collapse_button_pos;
5164 if (has_close_button)
5165 {
5166 pad_r += button_sz;
5167 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5168 }
5169 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
5170 {
5171 pad_r += button_sz;
5172 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y);
5173 }
5174 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
5175 {
5176 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y);
5177 pad_l += button_sz;
5178 }
5179
5180 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
5181 if (has_collapse_button)
5182 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
5183 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
5184
5185 // Close button
5186 if (has_close_button)
5187 if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
5188 *p_open = false;
5189
5190 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5191 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5192 window->DC.ItemFlags = item_flags_backup;
5193
5194 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5195 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
5196 const char* UNSAVED_DOCUMENT_MARKER = "*";
5197 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5198 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5199
5200 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
5201 // while uncentered title text will still reach edges correct.
5202 if (pad_l > style.FramePadding.x)
5203 pad_l += g.Style.ItemInnerSpacing.x;
5204 if (pad_r > style.FramePadding.x)
5205 pad_r += g.Style.ItemInnerSpacing.x;
5206 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
5207 {
5208 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
5209 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
5210 pad_l = ImMax(pad_l, pad_extend * centerness);
5211 pad_r = ImMax(pad_r, pad_extend * centerness);
5212 }
5213
5214 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);
5215 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y);
5216 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
5217 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
5218 if (flags & ImGuiWindowFlags_UnsavedDocument)
5219 {
5220 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);
5221 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f));
5222 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r);
5223 }
5224 }
5225
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)5226 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
5227 {
5228 window->ParentWindow = parent_window;
5229 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
5230 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
5231 window->RootWindow = parent_window->RootWindow;
5232 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
5233 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
5234 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
5235 {
5236 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
5237 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
5238 }
5239 }
5240
5241 // Push a new Dear ImGui window to add widgets to.
5242 // - 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.
5243 // - Begin/End can be called multiple times during the frame with the same window name to append content.
5244 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
5245 // 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.
5246 // - 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.
5247 // - 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)5248 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
5249 {
5250 ImGuiContext& g = *GImGui;
5251 const ImGuiStyle& style = g.Style;
5252 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
5253 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame()
5254 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
5255
5256 // Find or create
5257 ImGuiWindow* window = FindWindowByName(name);
5258 const bool window_just_created = (window == NULL);
5259 if (window_just_created)
5260 {
5261 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.
5262 window = CreateNewWindow(name, size_on_first_use, flags);
5263 }
5264
5265 // Automatically disable manual moving/resizing when NoInputs is set
5266 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
5267 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
5268
5269 if (flags & ImGuiWindowFlags_NavFlattened)
5270 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
5271
5272 const int current_frame = g.FrameCount;
5273 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
5274
5275 // Update the Appearing flag
5276 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
5277 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
5278 if (flags & ImGuiWindowFlags_Popup)
5279 {
5280 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5281 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
5282 window_just_activated_by_user |= (window != popup_ref.Window);
5283 }
5284 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
5285 if (window->Appearing)
5286 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
5287
5288 // Update Flags, LastFrameActive, BeginOrderXXX fields
5289 if (first_begin_of_the_frame)
5290 {
5291 window->Flags = (ImGuiWindowFlags)flags;
5292 window->LastFrameActive = current_frame;
5293 window->LastTimeActive = (float)g.Time;
5294 window->BeginOrderWithinParent = 0;
5295 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
5296 }
5297 else
5298 {
5299 flags = window->Flags;
5300 }
5301
5302 // 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
5303 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
5304 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
5305 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
5306
5307 // We allow window memory to be compacted so recreate the base stack when needed.
5308 if (window->IDStack.Size == 0)
5309 window->IDStack.push_back(window->ID);
5310
5311 // Add to stack
5312 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
5313 g.CurrentWindowStack.push_back(window);
5314 g.CurrentWindow = NULL;
5315 ErrorCheckBeginEndCompareStacksSize(window, true);
5316 if (flags & ImGuiWindowFlags_Popup)
5317 {
5318 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
5319 popup_ref.Window = window;
5320 g.BeginPopupStack.push_back(popup_ref);
5321 window->PopupId = popup_ref.PopupId;
5322 }
5323
5324 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
5325 window->NavLastIds[0] = 0;
5326
5327 // Process SetNextWindow***() calls
5328 bool window_pos_set_by_api = false;
5329 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
5330 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)
5331 {
5332 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
5333 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
5334 {
5335 // May be processed on the next frame if this is our first frame and we are measuring size
5336 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
5337 window->SetWindowPosVal = g.NextWindowData.PosVal;
5338 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
5339 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5340 }
5341 else
5342 {
5343 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
5344 }
5345 }
5346 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)
5347 {
5348 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
5349 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
5350 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
5351 }
5352 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)
5353 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
5354 else if (first_begin_of_the_frame)
5355 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
5356 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)
5357 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
5358 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)
5359 FocusWindow(window);
5360 if (window->Appearing)
5361 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
5362
5363 // When reusing window again multiple times a frame, just append content (don't need to setup again)
5364 if (first_begin_of_the_frame)
5365 {
5366 // Initialize
5367 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
5368 UpdateWindowParentAndRootLinks(window, flags, parent_window);
5369
5370 window->Active = true;
5371 window->HasCloseButton = (p_open != NULL);
5372 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
5373 window->IDStack.resize(1);
5374
5375 // Restore buffer capacity when woken from a compacted state, to avoid
5376 if (window->MemoryCompacted)
5377 GcAwakeTransientWindowBuffers(window);
5378
5379 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
5380 // 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.
5381 bool window_title_visible_elsewhere = false;
5382 if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
5383 window_title_visible_elsewhere = true;
5384 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
5385 {
5386 size_t buf_len = (size_t)window->NameBufLen;
5387 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
5388 window->NameBufLen = (int)buf_len;
5389 }
5390
5391 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
5392
5393 // Update contents size from last frame for auto-fitting (or use explicit size)
5394 window->ContentSize = CalcWindowContentSize(window);
5395 if (window->HiddenFramesCanSkipItems > 0)
5396 window->HiddenFramesCanSkipItems--;
5397 if (window->HiddenFramesCannotSkipItems > 0)
5398 window->HiddenFramesCannotSkipItems--;
5399
5400 // Hide new windows for one frame until they calculate their size
5401 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
5402 window->HiddenFramesCannotSkipItems = 1;
5403
5404 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
5405 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
5406 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
5407 {
5408 window->HiddenFramesCannotSkipItems = 1;
5409 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
5410 {
5411 if (!window_size_x_set_by_api)
5412 window->Size.x = window->SizeFull.x = 0.f;
5413 if (!window_size_y_set_by_api)
5414 window->Size.y = window->SizeFull.y = 0.f;
5415 window->ContentSize = ImVec2(0.f, 0.f);
5416 }
5417 }
5418
5419 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
5420 SetCurrentWindow(window);
5421
5422 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
5423
5424 if (flags & ImGuiWindowFlags_ChildWindow)
5425 window->WindowBorderSize = style.ChildBorderSize;
5426 else
5427 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
5428 window->WindowPadding = style.WindowPadding;
5429 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
5430 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
5431 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
5432 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
5433
5434 // Collapse window by double-clicking on title bar
5435 // 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
5436 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
5437 {
5438 // 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.
5439 ImRect title_bar_rect = window->TitleBarRect();
5440 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
5441 window->WantCollapseToggle = true;
5442 if (window->WantCollapseToggle)
5443 {
5444 window->Collapsed = !window->Collapsed;
5445 MarkIniSettingsDirty(window);
5446 FocusWindow(window);
5447 }
5448 }
5449 else
5450 {
5451 window->Collapsed = false;
5452 }
5453 window->WantCollapseToggle = false;
5454
5455 // SIZE
5456
5457 // Calculate auto-fit size, handle automatic resize
5458 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize);
5459 bool use_current_size_for_scrollbar_x = window_just_created;
5460 bool use_current_size_for_scrollbar_y = window_just_created;
5461 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
5462 {
5463 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
5464 if (!window_size_x_set_by_api)
5465 {
5466 window->SizeFull.x = size_auto_fit.x;
5467 use_current_size_for_scrollbar_x = true;
5468 }
5469 if (!window_size_y_set_by_api)
5470 {
5471 window->SizeFull.y = size_auto_fit.y;
5472 use_current_size_for_scrollbar_y = true;
5473 }
5474 }
5475 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
5476 {
5477 // Auto-fit may only grow window during the first few frames
5478 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
5479 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
5480 {
5481 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
5482 use_current_size_for_scrollbar_x = true;
5483 }
5484 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
5485 {
5486 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
5487 use_current_size_for_scrollbar_y = true;
5488 }
5489 if (!window->Collapsed)
5490 MarkIniSettingsDirty(window);
5491 }
5492
5493 // Apply minimum/maximum window size constraints and final size
5494 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
5495 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
5496
5497 // Decoration size
5498 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
5499
5500 // POSITION
5501
5502 // Popup latch its initial position, will position itself when it appears next frame
5503 if (window_just_activated_by_user)
5504 {
5505 window->AutoPosLastDirection = ImGuiDir_None;
5506 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
5507 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
5508 }
5509
5510 // Position child window
5511 if (flags & ImGuiWindowFlags_ChildWindow)
5512 {
5513 IM_ASSERT(parent_window && parent_window->Active);
5514 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
5515 parent_window->DC.ChildWindows.push_back(window);
5516 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
5517 window->Pos = parent_window->DC.CursorPos;
5518 }
5519
5520 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
5521 if (window_pos_with_pivot)
5522 SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
5523 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5524 window->Pos = FindBestWindowPosForPopup(window);
5525 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5526 window->Pos = FindBestWindowPosForPopup(window);
5527 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5528 window->Pos = FindBestWindowPosForPopup(window);
5529
5530 // Clamp position/size so window stays visible within its viewport or monitor
5531
5532 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5533 ImRect viewport_rect(GetViewportRect());
5534 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5535 {
5536 if (g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
5537 {
5538 ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5539 ClampWindowRect(window, viewport_rect, clamp_padding);
5540 }
5541 }
5542 window->Pos = ImFloor(window->Pos);
5543
5544 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5545 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5546
5547 // Apply window focus (new and reactivated windows are moved to front)
5548 bool want_focus = false;
5549 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5550 {
5551 if (flags & ImGuiWindowFlags_Popup)
5552 want_focus = true;
5553 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
5554 want_focus = true;
5555 }
5556
5557 // Handle manual resize: Resize Grips, Borders, Gamepad
5558 int border_held = -1;
5559 ImU32 resize_grip_col[4] = {};
5560 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
5561 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
5562 if (!window->Collapsed)
5563 if (UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]))
5564 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true;
5565 window->ResizeBorderHeld = (signed char)border_held;
5566
5567 // SCROLLBAR VISIBILITY
5568
5569 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
5570 if (!window->Collapsed)
5571 {
5572 // When reading the current size we need to read it after size constraints have been applied.
5573 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again.
5574 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height);
5575 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes;
5576 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
5577 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
5578 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
5579 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
5580 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
5581 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));
5582 if (window->ScrollbarX && !window->ScrollbarY)
5583 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);
5584 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
5585 }
5586
5587 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
5588 // Update various regions. Variables they depends on should be set above in this function.
5589 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
5590
5591 // Outer rectangle
5592 // Not affected by window border size. Used by:
5593 // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
5594 // - Begin() initial clipping rect for drawing window background and borders.
5595 // - Begin() clipping whole child
5596 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
5597 const ImRect outer_rect = window->Rect();
5598 const ImRect title_bar_rect = window->TitleBarRect();
5599 window->OuterRectClipped = outer_rect;
5600 window->OuterRectClipped.ClipWith(host_rect);
5601
5602 // Inner rectangle
5603 // Not affected by window border size. Used by:
5604 // - InnerClipRect
5605 // - ScrollToBringRectIntoView()
5606 // - NavUpdatePageUpPageDown()
5607 // - Scrollbar()
5608 window->InnerRect.Min.x = window->Pos.x;
5609 window->InnerRect.Min.y = window->Pos.y + decoration_up_height;
5610 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x;
5611 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y;
5612
5613 // Inner clipping rectangle.
5614 // Will extend a little bit outside the normal work region.
5615 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
5616 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5617 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5618 // Affected by window/frame border size. Used by:
5619 // - Begin() initial clip rect
5620 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5621 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5622 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size);
5623 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize));
5624 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);
5625 window->InnerClipRect.ClipWithFull(host_rect);
5626
5627 // Default item width. Make it proportional to window size if window manually resizes
5628 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5629 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f);
5630 else
5631 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f);
5632
5633 // SCROLLING
5634
5635 // Lock down maximum scrolling
5636 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
5637 // for right/bottom aligned items without creating a scrollbar.
5638 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
5639 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
5640
5641 // Apply scrolling
5642 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5643 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5644
5645 // DRAWING
5646
5647 // Setup draw list and outer clipping rectangle
5648 window->DrawList->Clear();
5649 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5650 PushClipRect(host_rect.Min, host_rect.Max, false);
5651
5652 // Draw modal window background (darkens what is behind them, all viewports)
5653 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0;
5654 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5655 if (dim_bg_for_modal || dim_bg_for_window_list)
5656 {
5657 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5658 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5659 }
5660
5661 // Draw navigation selection/windowing rectangle background
5662 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5663 {
5664 ImRect bb = window->Rect();
5665 bb.Expand(g.FontSize);
5666 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5667 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5668 }
5669
5670 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call.
5671 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
5672 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child.
5673 // We also disabled this when we have dimming overlay behind this specific one child.
5674 // 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.
5675 bool render_decorations_in_parent = false;
5676 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5677 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0)
5678 render_decorations_in_parent = true;
5679 if (render_decorations_in_parent)
5680 window->DrawList = parent_window->DrawList;
5681
5682 // Handle title bar, scrollbar, resize grips and resize borders
5683 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5684 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5685 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size);
5686
5687 if (render_decorations_in_parent)
5688 window->DrawList = &window->DrawListInst;
5689
5690 // Draw navigation selection/windowing rectangle border
5691 if (g.NavWindowingTargetAnim == window)
5692 {
5693 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5694 ImRect bb = window->Rect();
5695 bb.Expand(g.FontSize);
5696 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5697 {
5698 bb.Expand(-g.FontSize - 1.0f);
5699 rounding = window->WindowRounding;
5700 }
5701 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5702 }
5703
5704 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
5705
5706 // Work rectangle.
5707 // Affected by window padding and border size. Used by:
5708 // - Columns() for right-most edge
5709 // - TreeNode(), CollapsingHeader() for right-most edge
5710 // - BeginTabBar() for right-most edge
5711 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
5712 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
5713 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));
5714 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));
5715 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
5716 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
5717 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
5718 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
5719
5720 // [LEGACY] Content Region
5721 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5722 // Used by:
5723 // - Mouse wheel scrolling + many other things
5724 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5725 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height;
5726 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));
5727 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));
5728
5729 // Setup drawing context
5730 // (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.)
5731 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5732 window->DC.GroupOffset.x = 0.0f;
5733 window->DC.ColumnsOffset.x = 0.0f;
5734 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y);
5735 window->DC.CursorPos = window->DC.CursorStartPos;
5736 window->DC.CursorPosPrevLine = window->DC.CursorPos;
5737 window->DC.CursorMaxPos = window->DC.CursorStartPos;
5738 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5739 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5740 window->DC.NavHideHighlightOneFrame = false;
5741 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f);
5742 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5743 window->DC.NavLayerActiveMaskNext = 0x00;
5744 window->DC.MenuBarAppending = false;
5745 window->DC.ChildWindows.resize(0);
5746 window->DC.LayoutType = ImGuiLayoutType_Vertical;
5747 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5748 window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1;
5749 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5750 window->DC.ItemWidth = window->ItemWidthDefault;
5751 window->DC.TextWrapPos = -1.0f; // disabled
5752 window->DC.ItemFlagsStack.resize(0);
5753 window->DC.ItemWidthStack.resize(0);
5754 window->DC.TextWrapPosStack.resize(0);
5755 window->DC.CurrentColumns = NULL;
5756 window->DC.TreeDepth = 0;
5757 window->DC.TreeMayJumpToParentOnPopMask = 0x00;
5758 window->DC.StateStorage = &window->StateStorage;
5759 window->DC.GroupStack.resize(0);
5760 window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5761
5762 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5763 {
5764 window->DC.ItemFlags = parent_window->DC.ItemFlags;
5765 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5766 }
5767
5768 if (window->AutoFitFramesX > 0)
5769 window->AutoFitFramesX--;
5770 if (window->AutoFitFramesY > 0)
5771 window->AutoFitFramesY--;
5772
5773 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5774 if (want_focus)
5775 {
5776 FocusWindow(window);
5777 NavInitWindow(window, false);
5778 }
5779
5780 // Title bar
5781 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5782 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open);
5783
5784 // Pressing CTRL+C while holding on a window copy its content to the clipboard
5785 // 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.
5786 // Maybe we can support CTRL+C on every element?
5787 /*
5788 if (g.ActiveId == move_id)
5789 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5790 LogToClipboard();
5791 */
5792
5793 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
5794 // This is useful to allow creating context menus on title bar only, etc.
5795 window->DC.LastItemId = window->MoveId;
5796 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5797 window->DC.LastItemRect = title_bar_rect;
5798 #ifdef IMGUI_ENABLE_TEST_ENGINE
5799 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
5800 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId);
5801 #endif
5802 }
5803 else
5804 {
5805 // Append
5806 SetCurrentWindow(window);
5807 }
5808
5809 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5810
5811 // 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)
5812 if (first_begin_of_the_frame)
5813 window->WriteAccessed = false;
5814
5815 window->BeginCount++;
5816 g.NextWindowData.ClearFlags();
5817
5818 if (flags & ImGuiWindowFlags_ChildWindow)
5819 {
5820 // Child window can be out of sight and have "negative" clip windows.
5821 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5822 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5823 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5824 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5825 window->HiddenFramesCanSkipItems = 1;
5826
5827 // Hide along with parent or if parent is collapsed
5828 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
5829 window->HiddenFramesCanSkipItems = 1;
5830 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))
5831 window->HiddenFramesCannotSkipItems = 1;
5832 }
5833
5834 // 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)
5835 if (style.Alpha <= 0.0f)
5836 window->HiddenFramesCanSkipItems = 1;
5837
5838 // Update the Hidden flag
5839 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
5840
5841 // Update the SkipItems flag, used to early out of all items functions (no layout required)
5842 bool skip_items = false;
5843 if (window->Collapsed || !window->Active || window->Hidden)
5844 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
5845 skip_items = true;
5846 window->SkipItems = skip_items;
5847
5848 return !skip_items;
5849 }
5850
End()5851 void ImGui::End()
5852 {
5853 ImGuiContext& g = *GImGui;
5854 ImGuiWindow* window = g.CurrentWindow;
5855
5856 // Error checking: verify that user hasn't called End() too many times!
5857 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
5858 {
5859 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
5860 return;
5861 }
5862 IM_ASSERT(g.CurrentWindowStack.Size > 0);
5863
5864 // Error checking: verify that user doesn't directly call End() on a child window.
5865 if (window->Flags & ImGuiWindowFlags_ChildWindow)
5866 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!");
5867
5868 // Close anything that is open
5869 if (window->DC.CurrentColumns)
5870 EndColumns();
5871 PopClipRect(); // Inner window clip rectangle
5872
5873 // Stop logging
5874 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5875 LogFinish();
5876
5877 // Pop from window stack
5878 g.CurrentWindowStack.pop_back();
5879 if (window->Flags & ImGuiWindowFlags_Popup)
5880 g.BeginPopupStack.pop_back();
5881 ErrorCheckBeginEndCompareStacksSize(window, false);
5882 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5883 }
5884
BringWindowToFocusFront(ImGuiWindow * window)5885 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5886 {
5887 ImGuiContext& g = *GImGui;
5888 if (g.WindowsFocusOrder.back() == window)
5889 return;
5890 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window
5891 if (g.WindowsFocusOrder[i] == window)
5892 {
5893 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5894 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5895 break;
5896 }
5897 }
5898
BringWindowToDisplayFront(ImGuiWindow * window)5899 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5900 {
5901 ImGuiContext& g = *GImGui;
5902 ImGuiWindow* current_front_window = g.Windows.back();
5903 if (current_front_window == window || current_front_window->RootWindow == window)
5904 return;
5905 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
5906 if (g.Windows[i] == window)
5907 {
5908 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5909 g.Windows[g.Windows.Size - 1] = window;
5910 break;
5911 }
5912 }
5913
BringWindowToDisplayBack(ImGuiWindow * window)5914 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5915 {
5916 ImGuiContext& g = *GImGui;
5917 if (g.Windows[0] == window)
5918 return;
5919 for (int i = 0; i < g.Windows.Size; i++)
5920 if (g.Windows[i] == window)
5921 {
5922 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5923 g.Windows[0] = window;
5924 break;
5925 }
5926 }
5927
5928 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5929 void ImGui::FocusWindow(ImGuiWindow* window)
5930 {
5931 ImGuiContext& g = *GImGui;
5932
5933 if (g.NavWindow != window)
5934 {
5935 g.NavWindow = window;
5936 if (window && g.NavDisableMouseHover)
5937 g.NavMousePosDirty = true;
5938 g.NavInitRequest = false;
5939 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5940 g.NavIdIsAlive = false;
5941 g.NavLayer = ImGuiNavLayer_Main;
5942 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
5943 }
5944
5945 // Close popups if any
5946 ClosePopupsOverWindow(window, false);
5947
5948 // Passing NULL allow to disable keyboard focus
5949 if (!window)
5950 return;
5951
5952 // Move the root window to the top of the pile
5953 if (window->RootWindow)
5954 window = window->RootWindow;
5955
5956 // Steal focus on active widgets
5957 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5958 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5959 ClearActiveID();
5960
5961 // Bring to front
5962 BringWindowToFocusFront(window);
5963 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5964 BringWindowToDisplayFront(window);
5965 }
5966
FocusTopMostWindowUnderOne(ImGuiWindow * under_this_window,ImGuiWindow * ignore_window)5967 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window)
5968 {
5969 ImGuiContext& g = *GImGui;
5970
5971 int start_idx = g.WindowsFocusOrder.Size - 1;
5972 if (under_this_window != NULL)
5973 {
5974 int under_this_window_idx = FindWindowFocusIndex(under_this_window);
5975 if (under_this_window_idx != -1)
5976 start_idx = under_this_window_idx - 1;
5977 }
5978 for (int i = start_idx; i >= 0; i--)
5979 {
5980 // 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.
5981 ImGuiWindow* window = g.WindowsFocusOrder[i];
5982 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5983 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5984 {
5985 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5986 FocusWindow(focus_window);
5987 return;
5988 }
5989 }
5990 FocusWindow(NULL);
5991 }
5992
SetNextItemWidth(float item_width)5993 void ImGui::SetNextItemWidth(float item_width)
5994 {
5995 ImGuiContext& g = *GImGui;
5996 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;
5997 g.NextItemData.Width = item_width;
5998 }
5999
PushItemWidth(float item_width)6000 void ImGui::PushItemWidth(float item_width)
6001 {
6002 ImGuiContext& g = *GImGui;
6003 ImGuiWindow* window = g.CurrentWindow;
6004 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
6005 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
6006 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6007 }
6008
PushMultiItemsWidths(int components,float w_full)6009 void ImGui::PushMultiItemsWidths(int components, float w_full)
6010 {
6011 ImGuiContext& g = *GImGui;
6012 ImGuiWindow* window = g.CurrentWindow;
6013 const ImGuiStyle& style = g.Style;
6014 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
6015 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
6016 window->DC.ItemWidthStack.push_back(w_item_last);
6017 for (int i = 0; i < components-1; i++)
6018 window->DC.ItemWidthStack.push_back(w_item_one);
6019 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
6020 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;
6021 }
6022
PopItemWidth()6023 void ImGui::PopItemWidth()
6024 {
6025 ImGuiWindow* window = GetCurrentWindow();
6026 window->DC.ItemWidthStack.pop_back();
6027 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
6028 }
6029
6030 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
6031 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
CalcItemWidth()6032 float ImGui::CalcItemWidth()
6033 {
6034 ImGuiContext& g = *GImGui;
6035 ImGuiWindow* window = g.CurrentWindow;
6036 float w;
6037 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)
6038 w = g.NextItemData.Width;
6039 else
6040 w = window->DC.ItemWidth;
6041 if (w < 0.0f)
6042 {
6043 float region_max_x = GetContentRegionMaxAbs().x;
6044 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);
6045 }
6046 w = IM_FLOOR(w);
6047 return w;
6048 }
6049
6050 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
6051 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
6052 // Note that only CalcItemWidth() is publicly exposed.
6053 // 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)6054 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
6055 {
6056 ImGuiWindow* window = GImGui->CurrentWindow;
6057
6058 ImVec2 region_max;
6059 if (size.x < 0.0f || size.y < 0.0f)
6060 region_max = GetContentRegionMaxAbs();
6061
6062 if (size.x == 0.0f)
6063 size.x = default_w;
6064 else if (size.x < 0.0f)
6065 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);
6066
6067 if (size.y == 0.0f)
6068 size.y = default_h;
6069 else if (size.y < 0.0f)
6070 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);
6071
6072 return size;
6073 }
6074
SetCurrentFont(ImFont * font)6075 void ImGui::SetCurrentFont(ImFont* font)
6076 {
6077 ImGuiContext& g = *GImGui;
6078 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
6079 IM_ASSERT(font->Scale > 0.0f);
6080 g.Font = font;
6081 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);
6082 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
6083
6084 ImFontAtlas* atlas = g.Font->ContainerAtlas;
6085 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
6086 g.DrawListSharedData.Font = g.Font;
6087 g.DrawListSharedData.FontSize = g.FontSize;
6088 }
6089
PushFont(ImFont * font)6090 void ImGui::PushFont(ImFont* font)
6091 {
6092 ImGuiContext& g = *GImGui;
6093 if (!font)
6094 font = GetDefaultFont();
6095 SetCurrentFont(font);
6096 g.FontStack.push_back(font);
6097 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
6098 }
6099
PopFont()6100 void ImGui::PopFont()
6101 {
6102 ImGuiContext& g = *GImGui;
6103 g.CurrentWindow->DrawList->PopTextureID();
6104 g.FontStack.pop_back();
6105 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
6106 }
6107
PushItemFlag(ImGuiItemFlags option,bool enabled)6108 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
6109 {
6110 ImGuiWindow* window = GetCurrentWindow();
6111 if (enabled)
6112 window->DC.ItemFlags |= option;
6113 else
6114 window->DC.ItemFlags &= ~option;
6115 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
6116 }
6117
PopItemFlag()6118 void ImGui::PopItemFlag()
6119 {
6120 ImGuiWindow* window = GetCurrentWindow();
6121 window->DC.ItemFlagsStack.pop_back();
6122 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
6123 }
6124
6125 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)6126 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
6127 {
6128 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
6129 }
6130
PopAllowKeyboardFocus()6131 void ImGui::PopAllowKeyboardFocus()
6132 {
6133 PopItemFlag();
6134 }
6135
PushButtonRepeat(bool repeat)6136 void ImGui::PushButtonRepeat(bool repeat)
6137 {
6138 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
6139 }
6140
PopButtonRepeat()6141 void ImGui::PopButtonRepeat()
6142 {
6143 PopItemFlag();
6144 }
6145
PushTextWrapPos(float wrap_pos_x)6146 void ImGui::PushTextWrapPos(float wrap_pos_x)
6147 {
6148 ImGuiWindow* window = GetCurrentWindow();
6149 window->DC.TextWrapPos = wrap_pos_x;
6150 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
6151 }
6152
PopTextWrapPos()6153 void ImGui::PopTextWrapPos()
6154 {
6155 ImGuiWindow* window = GetCurrentWindow();
6156 window->DC.TextWrapPosStack.pop_back();
6157 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
6158 }
6159
6160 // 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)6161 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
6162 {
6163 ImGuiContext& g = *GImGui;
6164 ImGuiColorMod backup;
6165 backup.Col = idx;
6166 backup.BackupValue = g.Style.Colors[idx];
6167 g.ColorModifiers.push_back(backup);
6168 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
6169 }
6170
PushStyleColor(ImGuiCol idx,const ImVec4 & col)6171 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
6172 {
6173 ImGuiContext& g = *GImGui;
6174 ImGuiColorMod backup;
6175 backup.Col = idx;
6176 backup.BackupValue = g.Style.Colors[idx];
6177 g.ColorModifiers.push_back(backup);
6178 g.Style.Colors[idx] = col;
6179 }
6180
PopStyleColor(int count)6181 void ImGui::PopStyleColor(int count)
6182 {
6183 ImGuiContext& g = *GImGui;
6184 while (count > 0)
6185 {
6186 ImGuiColorMod& backup = g.ColorModifiers.back();
6187 g.Style.Colors[backup.Col] = backup.BackupValue;
6188 g.ColorModifiers.pop_back();
6189 count--;
6190 }
6191 }
6192
6193 struct ImGuiStyleVarInfo
6194 {
6195 ImGuiDataType Type;
6196 ImU32 Count;
6197 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo6198 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
6199 };
6200
6201 static const ImGuiStyleVarInfo GStyleVarInfo[] =
6202 {
6203 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
6204 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
6205 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
6206 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
6207 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
6208 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
6209 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
6210 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
6211 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
6212 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
6213 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
6214 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
6215 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
6216 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
6217 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
6218 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
6219 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
6220 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
6221 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
6222 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
6223 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
6224 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
6225 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
6226 };
6227
GetStyleVarInfo(ImGuiStyleVar idx)6228 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
6229 {
6230 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
6231 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
6232 return &GStyleVarInfo[idx];
6233 }
6234
PushStyleVar(ImGuiStyleVar idx,float val)6235 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
6236 {
6237 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6238 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
6239 {
6240 ImGuiContext& g = *GImGui;
6241 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
6242 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6243 *pvar = val;
6244 return;
6245 }
6246 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!");
6247 }
6248
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)6249 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
6250 {
6251 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
6252 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
6253 {
6254 ImGuiContext& g = *GImGui;
6255 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
6256 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
6257 *pvar = val;
6258 return;
6259 }
6260 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!");
6261 }
6262
PopStyleVar(int count)6263 void ImGui::PopStyleVar(int count)
6264 {
6265 ImGuiContext& g = *GImGui;
6266 while (count > 0)
6267 {
6268 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
6269 ImGuiStyleMod& backup = g.StyleModifiers.back();
6270 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
6271 void* data = info->GetVarPtr(&g.Style);
6272 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
6273 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
6274 g.StyleModifiers.pop_back();
6275 count--;
6276 }
6277 }
6278
GetStyleColorName(ImGuiCol idx)6279 const char* ImGui::GetStyleColorName(ImGuiCol idx)
6280 {
6281 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
6282 switch (idx)
6283 {
6284 case ImGuiCol_Text: return "Text";
6285 case ImGuiCol_TextDisabled: return "TextDisabled";
6286 case ImGuiCol_WindowBg: return "WindowBg";
6287 case ImGuiCol_ChildBg: return "ChildBg";
6288 case ImGuiCol_PopupBg: return "PopupBg";
6289 case ImGuiCol_Border: return "Border";
6290 case ImGuiCol_BorderShadow: return "BorderShadow";
6291 case ImGuiCol_FrameBg: return "FrameBg";
6292 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
6293 case ImGuiCol_FrameBgActive: return "FrameBgActive";
6294 case ImGuiCol_TitleBg: return "TitleBg";
6295 case ImGuiCol_TitleBgActive: return "TitleBgActive";
6296 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
6297 case ImGuiCol_MenuBarBg: return "MenuBarBg";
6298 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
6299 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
6300 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
6301 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
6302 case ImGuiCol_CheckMark: return "CheckMark";
6303 case ImGuiCol_SliderGrab: return "SliderGrab";
6304 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
6305 case ImGuiCol_Button: return "Button";
6306 case ImGuiCol_ButtonHovered: return "ButtonHovered";
6307 case ImGuiCol_ButtonActive: return "ButtonActive";
6308 case ImGuiCol_Header: return "Header";
6309 case ImGuiCol_HeaderHovered: return "HeaderHovered";
6310 case ImGuiCol_HeaderActive: return "HeaderActive";
6311 case ImGuiCol_Separator: return "Separator";
6312 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
6313 case ImGuiCol_SeparatorActive: return "SeparatorActive";
6314 case ImGuiCol_ResizeGrip: return "ResizeGrip";
6315 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
6316 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
6317 case ImGuiCol_Tab: return "Tab";
6318 case ImGuiCol_TabHovered: return "TabHovered";
6319 case ImGuiCol_TabActive: return "TabActive";
6320 case ImGuiCol_TabUnfocused: return "TabUnfocused";
6321 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
6322 case ImGuiCol_PlotLines: return "PlotLines";
6323 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
6324 case ImGuiCol_PlotHistogram: return "PlotHistogram";
6325 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
6326 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
6327 case ImGuiCol_DragDropTarget: return "DragDropTarget";
6328 case ImGuiCol_NavHighlight: return "NavHighlight";
6329 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
6330 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
6331 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
6332 }
6333 IM_ASSERT(0);
6334 return "Unknown";
6335 }
6336
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)6337 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
6338 {
6339 if (window->RootWindow == potential_parent)
6340 return true;
6341 while (window != NULL)
6342 {
6343 if (window == potential_parent)
6344 return true;
6345 window = window->ParentWindow;
6346 }
6347 return false;
6348 }
6349
IsWindowHovered(ImGuiHoveredFlags flags)6350 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
6351 {
6352 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
6353 ImGuiContext& g = *GImGui;
6354
6355 if (flags & ImGuiHoveredFlags_AnyWindow)
6356 {
6357 if (g.HoveredWindow == NULL)
6358 return false;
6359 }
6360 else
6361 {
6362 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
6363 {
6364 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
6365 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
6366 return false;
6367 break;
6368 case ImGuiHoveredFlags_RootWindow:
6369 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
6370 return false;
6371 break;
6372 case ImGuiHoveredFlags_ChildWindows:
6373 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
6374 return false;
6375 break;
6376 default:
6377 if (g.HoveredWindow != g.CurrentWindow)
6378 return false;
6379 break;
6380 }
6381 }
6382
6383 if (!IsWindowContentHoverable(g.HoveredWindow, flags))
6384 return false;
6385 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
6386 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
6387 return false;
6388 return true;
6389 }
6390
IsWindowFocused(ImGuiFocusedFlags flags)6391 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
6392 {
6393 ImGuiContext& g = *GImGui;
6394
6395 if (flags & ImGuiFocusedFlags_AnyWindow)
6396 return g.NavWindow != NULL;
6397
6398 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
6399 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
6400 {
6401 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
6402 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
6403 case ImGuiFocusedFlags_RootWindow:
6404 return g.NavWindow == g.CurrentWindow->RootWindow;
6405 case ImGuiFocusedFlags_ChildWindows:
6406 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
6407 default:
6408 return g.NavWindow == g.CurrentWindow;
6409 }
6410 }
6411
6412 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
6413 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
6414 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)6415 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
6416 {
6417 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
6418 }
6419
GetWindowWidth()6420 float ImGui::GetWindowWidth()
6421 {
6422 ImGuiWindow* window = GImGui->CurrentWindow;
6423 return window->Size.x;
6424 }
6425
GetWindowHeight()6426 float ImGui::GetWindowHeight()
6427 {
6428 ImGuiWindow* window = GImGui->CurrentWindow;
6429 return window->Size.y;
6430 }
6431
GetWindowPos()6432 ImVec2 ImGui::GetWindowPos()
6433 {
6434 ImGuiContext& g = *GImGui;
6435 ImGuiWindow* window = g.CurrentWindow;
6436 return window->Pos;
6437 }
6438
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)6439 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
6440 {
6441 // Test condition (NB: bit 0 is always true) and clear flags for next time
6442 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
6443 return;
6444
6445 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6446 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6447 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
6448
6449 // Set
6450 const ImVec2 old_pos = window->Pos;
6451 window->Pos = ImFloor(pos);
6452 ImVec2 offset = window->Pos - old_pos;
6453 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
6454 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
6455 window->DC.CursorStartPos += offset;
6456 }
6457
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)6458 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
6459 {
6460 ImGuiWindow* window = GetCurrentWindowRead();
6461 SetWindowPos(window, pos, cond);
6462 }
6463
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)6464 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
6465 {
6466 if (ImGuiWindow* window = FindWindowByName(name))
6467 SetWindowPos(window, pos, cond);
6468 }
6469
GetWindowSize()6470 ImVec2 ImGui::GetWindowSize()
6471 {
6472 ImGuiWindow* window = GetCurrentWindowRead();
6473 return window->Size;
6474 }
6475
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)6476 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
6477 {
6478 // Test condition (NB: bit 0 is always true) and clear flags for next time
6479 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
6480 return;
6481
6482 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6483 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6484
6485 // Set
6486 if (size.x > 0.0f)
6487 {
6488 window->AutoFitFramesX = 0;
6489 window->SizeFull.x = IM_FLOOR(size.x);
6490 }
6491 else
6492 {
6493 window->AutoFitFramesX = 2;
6494 window->AutoFitOnlyGrows = false;
6495 }
6496 if (size.y > 0.0f)
6497 {
6498 window->AutoFitFramesY = 0;
6499 window->SizeFull.y = IM_FLOOR(size.y);
6500 }
6501 else
6502 {
6503 window->AutoFitFramesY = 2;
6504 window->AutoFitOnlyGrows = false;
6505 }
6506 }
6507
SetWindowSize(const ImVec2 & size,ImGuiCond cond)6508 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
6509 {
6510 SetWindowSize(GImGui->CurrentWindow, size, cond);
6511 }
6512
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)6513 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
6514 {
6515 if (ImGuiWindow* window = FindWindowByName(name))
6516 SetWindowSize(window, size, cond);
6517 }
6518
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)6519 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6520 {
6521 // Test condition (NB: bit 0 is always true) and clear flags for next time
6522 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6523 return;
6524 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6525
6526 // Set
6527 window->Collapsed = collapsed;
6528 }
6529
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6530 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6531 {
6532 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6533 }
6534
IsWindowCollapsed()6535 bool ImGui::IsWindowCollapsed()
6536 {
6537 ImGuiWindow* window = GetCurrentWindowRead();
6538 return window->Collapsed;
6539 }
6540
IsWindowAppearing()6541 bool ImGui::IsWindowAppearing()
6542 {
6543 ImGuiWindow* window = GetCurrentWindowRead();
6544 return window->Appearing;
6545 }
6546
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6547 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6548 {
6549 if (ImGuiWindow* window = FindWindowByName(name))
6550 SetWindowCollapsed(window, collapsed, cond);
6551 }
6552
SetWindowFocus()6553 void ImGui::SetWindowFocus()
6554 {
6555 FocusWindow(GImGui->CurrentWindow);
6556 }
6557
SetWindowFocus(const char * name)6558 void ImGui::SetWindowFocus(const char* name)
6559 {
6560 if (name)
6561 {
6562 if (ImGuiWindow* window = FindWindowByName(name))
6563 FocusWindow(window);
6564 }
6565 else
6566 {
6567 FocusWindow(NULL);
6568 }
6569 }
6570
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6571 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6572 {
6573 ImGuiContext& g = *GImGui;
6574 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6575 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;
6576 g.NextWindowData.PosVal = pos;
6577 g.NextWindowData.PosPivotVal = pivot;
6578 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6579 }
6580
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6581 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6582 {
6583 ImGuiContext& g = *GImGui;
6584 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6585 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;
6586 g.NextWindowData.SizeVal = size;
6587 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6588 }
6589
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6590 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6591 {
6592 ImGuiContext& g = *GImGui;
6593 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
6594 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6595 g.NextWindowData.SizeCallback = custom_callback;
6596 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6597 }
6598
6599 // Content size = inner scrollable rectangle, padded with WindowPadding.
6600 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
SetNextWindowContentSize(const ImVec2 & size)6601 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6602 {
6603 ImGuiContext& g = *GImGui;
6604 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;
6605 g.NextWindowData.ContentSizeVal = size;
6606 }
6607
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6608 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6609 {
6610 ImGuiContext& g = *GImGui;
6611 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6612 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;
6613 g.NextWindowData.CollapsedVal = collapsed;
6614 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6615 }
6616
SetNextWindowFocus()6617 void ImGui::SetNextWindowFocus()
6618 {
6619 ImGuiContext& g = *GImGui;
6620 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;
6621 }
6622
SetNextWindowBgAlpha(float alpha)6623 void ImGui::SetNextWindowBgAlpha(float alpha)
6624 {
6625 ImGuiContext& g = *GImGui;
6626 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;
6627 g.NextWindowData.BgAlphaVal = alpha;
6628 }
6629
6630 // FIXME: This is in window space (not screen space!). We should try to obsolete all those functions.
GetContentRegionMax()6631 ImVec2 ImGui::GetContentRegionMax()
6632 {
6633 ImGuiContext& g = *GImGui;
6634 ImGuiWindow* window = g.CurrentWindow;
6635 ImVec2 mx = window->ContentRegionRect.Max - window->Pos;
6636 if (window->DC.CurrentColumns)
6637 mx.x = window->WorkRect.Max.x - window->Pos.x;
6638 return mx;
6639 }
6640
6641 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.
GetContentRegionMaxAbs()6642 ImVec2 ImGui::GetContentRegionMaxAbs()
6643 {
6644 ImGuiContext& g = *GImGui;
6645 ImGuiWindow* window = g.CurrentWindow;
6646 ImVec2 mx = window->ContentRegionRect.Max;
6647 if (window->DC.CurrentColumns)
6648 mx.x = window->WorkRect.Max.x;
6649 return mx;
6650 }
6651
GetContentRegionAvail()6652 ImVec2 ImGui::GetContentRegionAvail()
6653 {
6654 ImGuiWindow* window = GImGui->CurrentWindow;
6655 return GetContentRegionMaxAbs() - window->DC.CursorPos;
6656 }
6657
6658 // In window space (not screen space!)
GetWindowContentRegionMin()6659 ImVec2 ImGui::GetWindowContentRegionMin()
6660 {
6661 ImGuiWindow* window = GImGui->CurrentWindow;
6662 return window->ContentRegionRect.Min - window->Pos;
6663 }
6664
GetWindowContentRegionMax()6665 ImVec2 ImGui::GetWindowContentRegionMax()
6666 {
6667 ImGuiWindow* window = GImGui->CurrentWindow;
6668 return window->ContentRegionRect.Max - window->Pos;
6669 }
6670
GetWindowContentRegionWidth()6671 float ImGui::GetWindowContentRegionWidth()
6672 {
6673 ImGuiWindow* window = GImGui->CurrentWindow;
6674 return window->ContentRegionRect.GetWidth();
6675 }
6676
GetTextLineHeight()6677 float ImGui::GetTextLineHeight()
6678 {
6679 ImGuiContext& g = *GImGui;
6680 return g.FontSize;
6681 }
6682
GetTextLineHeightWithSpacing()6683 float ImGui::GetTextLineHeightWithSpacing()
6684 {
6685 ImGuiContext& g = *GImGui;
6686 return g.FontSize + g.Style.ItemSpacing.y;
6687 }
6688
GetFrameHeight()6689 float ImGui::GetFrameHeight()
6690 {
6691 ImGuiContext& g = *GImGui;
6692 return g.FontSize + g.Style.FramePadding.y * 2.0f;
6693 }
6694
GetFrameHeightWithSpacing()6695 float ImGui::GetFrameHeightWithSpacing()
6696 {
6697 ImGuiContext& g = *GImGui;
6698 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6699 }
6700
GetWindowDrawList()6701 ImDrawList* ImGui::GetWindowDrawList()
6702 {
6703 ImGuiWindow* window = GetCurrentWindow();
6704 return window->DrawList;
6705 }
6706
GetFont()6707 ImFont* ImGui::GetFont()
6708 {
6709 return GImGui->Font;
6710 }
6711
GetFontSize()6712 float ImGui::GetFontSize()
6713 {
6714 return GImGui->FontSize;
6715 }
6716
GetFontTexUvWhitePixel()6717 ImVec2 ImGui::GetFontTexUvWhitePixel()
6718 {
6719 return GImGui->DrawListSharedData.TexUvWhitePixel;
6720 }
6721
SetWindowFontScale(float scale)6722 void ImGui::SetWindowFontScale(float scale)
6723 {
6724 ImGuiContext& g = *GImGui;
6725 ImGuiWindow* window = GetCurrentWindow();
6726 window->FontWindowScale = scale;
6727 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6728 }
6729
6730 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6731 // 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()6732 ImVec2 ImGui::GetCursorPos()
6733 {
6734 ImGuiWindow* window = GetCurrentWindowRead();
6735 return window->DC.CursorPos - window->Pos + window->Scroll;
6736 }
6737
GetCursorPosX()6738 float ImGui::GetCursorPosX()
6739 {
6740 ImGuiWindow* window = GetCurrentWindowRead();
6741 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6742 }
6743
GetCursorPosY()6744 float ImGui::GetCursorPosY()
6745 {
6746 ImGuiWindow* window = GetCurrentWindowRead();
6747 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6748 }
6749
SetCursorPos(const ImVec2 & local_pos)6750 void ImGui::SetCursorPos(const ImVec2& local_pos)
6751 {
6752 ImGuiWindow* window = GetCurrentWindow();
6753 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6754 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6755 }
6756
SetCursorPosX(float x)6757 void ImGui::SetCursorPosX(float x)
6758 {
6759 ImGuiWindow* window = GetCurrentWindow();
6760 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6761 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6762 }
6763
SetCursorPosY(float y)6764 void ImGui::SetCursorPosY(float y)
6765 {
6766 ImGuiWindow* window = GetCurrentWindow();
6767 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6768 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6769 }
6770
GetCursorStartPos()6771 ImVec2 ImGui::GetCursorStartPos()
6772 {
6773 ImGuiWindow* window = GetCurrentWindowRead();
6774 return window->DC.CursorStartPos - window->Pos;
6775 }
6776
GetCursorScreenPos()6777 ImVec2 ImGui::GetCursorScreenPos()
6778 {
6779 ImGuiWindow* window = GetCurrentWindowRead();
6780 return window->DC.CursorPos;
6781 }
6782
SetCursorScreenPos(const ImVec2 & pos)6783 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6784 {
6785 ImGuiWindow* window = GetCurrentWindow();
6786 window->DC.CursorPos = pos;
6787 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6788 }
6789
ActivateItem(ImGuiID id)6790 void ImGui::ActivateItem(ImGuiID id)
6791 {
6792 ImGuiContext& g = *GImGui;
6793 g.NavNextActivateId = id;
6794 }
6795
SetKeyboardFocusHere(int offset)6796 void ImGui::SetKeyboardFocusHere(int offset)
6797 {
6798 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6799 ImGuiContext& g = *GImGui;
6800 ImGuiWindow* window = g.CurrentWindow;
6801 g.FocusRequestNextWindow = window;
6802 g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset;
6803 g.FocusRequestNextCounterTab = INT_MAX;
6804 }
6805
SetItemDefaultFocus()6806 void ImGui::SetItemDefaultFocus()
6807 {
6808 ImGuiContext& g = *GImGui;
6809 ImGuiWindow* window = g.CurrentWindow;
6810 if (!window->Appearing)
6811 return;
6812 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6813 {
6814 g.NavInitRequest = false;
6815 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6816 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6817 NavUpdateAnyRequestFlag();
6818 if (!IsItemVisible())
6819 SetScrollHereY();
6820 }
6821 }
6822
SetStateStorage(ImGuiStorage * tree)6823 void ImGui::SetStateStorage(ImGuiStorage* tree)
6824 {
6825 ImGuiWindow* window = GImGui->CurrentWindow;
6826 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6827 }
6828
GetStateStorage()6829 ImGuiStorage* ImGui::GetStateStorage()
6830 {
6831 ImGuiWindow* window = GImGui->CurrentWindow;
6832 return window->DC.StateStorage;
6833 }
6834
PushID(const char * str_id)6835 void ImGui::PushID(const char* str_id)
6836 {
6837 ImGuiWindow* window = GImGui->CurrentWindow;
6838 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6839 }
6840
PushID(const char * str_id_begin,const char * str_id_end)6841 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6842 {
6843 ImGuiWindow* window = GImGui->CurrentWindow;
6844 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6845 }
6846
PushID(const void * ptr_id)6847 void ImGui::PushID(const void* ptr_id)
6848 {
6849 ImGuiWindow* window = GImGui->CurrentWindow;
6850 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6851 }
6852
PushID(int int_id)6853 void ImGui::PushID(int int_id)
6854 {
6855 ImGuiWindow* window = GImGui->CurrentWindow;
6856 window->IDStack.push_back(window->GetIDNoKeepAlive(int_id));
6857 }
6858
6859 // Push a given id value ignoring the ID stack as a seed.
PushOverrideID(ImGuiID id)6860 void ImGui::PushOverrideID(ImGuiID id)
6861 {
6862 ImGuiWindow* window = GImGui->CurrentWindow;
6863 window->IDStack.push_back(id);
6864 }
6865
PopID()6866 void ImGui::PopID()
6867 {
6868 ImGuiWindow* window = GImGui->CurrentWindow;
6869 window->IDStack.pop_back();
6870 }
6871
GetID(const char * str_id)6872 ImGuiID ImGui::GetID(const char* str_id)
6873 {
6874 ImGuiWindow* window = GImGui->CurrentWindow;
6875 return window->GetID(str_id);
6876 }
6877
GetID(const char * str_id_begin,const char * str_id_end)6878 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6879 {
6880 ImGuiWindow* window = GImGui->CurrentWindow;
6881 return window->GetID(str_id_begin, str_id_end);
6882 }
6883
GetID(const void * ptr_id)6884 ImGuiID ImGui::GetID(const void* ptr_id)
6885 {
6886 ImGuiWindow* window = GImGui->CurrentWindow;
6887 return window->GetID(ptr_id);
6888 }
6889
IsRectVisible(const ImVec2 & size)6890 bool ImGui::IsRectVisible(const ImVec2& size)
6891 {
6892 ImGuiWindow* window = GImGui->CurrentWindow;
6893 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6894 }
6895
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6896 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6897 {
6898 ImGuiWindow* window = GImGui->CurrentWindow;
6899 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6900 }
6901
6902 // 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()6903 void ImGui::BeginGroup()
6904 {
6905 ImGuiContext& g = *GImGui;
6906 ImGuiWindow* window = GetCurrentWindow();
6907
6908 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6909 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6910 group_data.BackupCursorPos = window->DC.CursorPos;
6911 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6912 group_data.BackupIndent = window->DC.Indent;
6913 group_data.BackupGroupOffset = window->DC.GroupOffset;
6914 group_data.BackupCurrLineSize = window->DC.CurrLineSize;
6915 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
6916 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6917 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6918 group_data.EmitItem = true;
6919
6920 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6921 window->DC.Indent = window->DC.GroupOffset;
6922 window->DC.CursorMaxPos = window->DC.CursorPos;
6923 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
6924 if (g.LogEnabled)
6925 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6926 }
6927
EndGroup()6928 void ImGui::EndGroup()
6929 {
6930 ImGuiContext& g = *GImGui;
6931 ImGuiWindow* window = GetCurrentWindow();
6932 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
6933
6934 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6935
6936 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));
6937
6938 window->DC.CursorPos = group_data.BackupCursorPos;
6939 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6940 window->DC.Indent = group_data.BackupIndent;
6941 window->DC.GroupOffset = group_data.BackupGroupOffset;
6942 window->DC.CurrLineSize = group_data.BackupCurrLineSize;
6943 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
6944 if (g.LogEnabled)
6945 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return
6946
6947 if (!group_data.EmitItem)
6948 {
6949 window->DC.GroupStack.pop_back();
6950 return;
6951 }
6952
6953 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.
6954 ItemSize(group_bb.GetSize());
6955 ItemAdd(group_bb, 0);
6956
6957 // 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.
6958 // 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.
6959 // Also if you grep for LastItemId you'll notice it is only used in that context.
6960 // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
6961 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
6962 const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive;
6963 if (group_contains_curr_active_id)
6964 window->DC.LastItemId = g.ActiveId;
6965 else if (group_contains_prev_active_id)
6966 window->DC.LastItemId = g.ActiveIdPreviousFrame;
6967 window->DC.LastItemRect = group_bb;
6968
6969 // Forward Edited flag
6970 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
6971 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
6972
6973 // Forward Deactivated flag
6974 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
6975 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)
6976 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated;
6977
6978 window->DC.GroupStack.pop_back();
6979 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
6980 }
6981
6982 // Gets back to previous line and continue with horizontal layout
6983 // offset_from_start_x == 0 : follow right after previous item
6984 // offset_from_start_x != 0 : align to specified x position (relative to window/group left)
6985 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
6986 // spacing_w >= 0 : enforce spacing amount
SameLine(float offset_from_start_x,float spacing_w)6987 void ImGui::SameLine(float offset_from_start_x, float spacing_w)
6988 {
6989 ImGuiWindow* window = GetCurrentWindow();
6990 if (window->SkipItems)
6991 return;
6992
6993 ImGuiContext& g = *GImGui;
6994 if (offset_from_start_x != 0.0f)
6995 {
6996 if (spacing_w < 0.0f) spacing_w = 0.0f;
6997 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6998 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6999 }
7000 else
7001 {
7002 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
7003 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
7004 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
7005 }
7006 window->DC.CurrLineSize = window->DC.PrevLineSize;
7007 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
7008 }
7009
Indent(float indent_w)7010 void ImGui::Indent(float indent_w)
7011 {
7012 ImGuiContext& g = *GImGui;
7013 ImGuiWindow* window = GetCurrentWindow();
7014 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7015 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7016 }
7017
Unindent(float indent_w)7018 void ImGui::Unindent(float indent_w)
7019 {
7020 ImGuiContext& g = *GImGui;
7021 ImGuiWindow* window = GetCurrentWindow();
7022 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
7023 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
7024 }
7025
7026
7027 //-----------------------------------------------------------------------------
7028 // [SECTION] ERROR CHECKING
7029 //-----------------------------------------------------------------------------
7030
ErrorCheckEndFrame()7031 static void ImGui::ErrorCheckEndFrame()
7032 {
7033 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
7034 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
7035 ImGuiContext& g = *GImGui;
7036 if (g.CurrentWindowStack.Size != 1)
7037 {
7038 if (g.CurrentWindowStack.Size > 1)
7039 {
7040 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
7041 while (g.CurrentWindowStack.Size > 1)
7042 End();
7043 }
7044 else
7045 {
7046 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
7047 }
7048 }
7049
7050 }
7051
7052 // Save and compare stack sizes on Begin()/End() to detect usage errors
7053 // Begin() calls this with write=true
7054 // End() calls this with write=false
ErrorCheckBeginEndCompareStacksSize(ImGuiWindow * window,bool write)7055 static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write)
7056 {
7057 ImGuiContext& g = *GImGui;
7058 short* p = &window->DC.StackSizesBackup[0];
7059
7060 // Window stacks
7061 // 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)
7062 { 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()
7063 { 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()
7064
7065 // Global stacks
7066 // 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.
7067 { 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()
7068 { 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()
7069 { 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()
7070 { 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()
7071 IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
7072 }
7073
7074
7075 //-----------------------------------------------------------------------------
7076 // [SECTION] SCROLLING
7077 //-----------------------------------------------------------------------------
7078
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)7079 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
7080 {
7081 ImGuiContext& g = *GImGui;
7082 ImVec2 scroll = window->Scroll;
7083 if (window->ScrollTarget.x < FLT_MAX)
7084 {
7085 float cr_x = window->ScrollTargetCenterRatio.x;
7086 float target_x = window->ScrollTarget.x;
7087 if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x)
7088 target_x = 0.0f;
7089 else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x)
7090 target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f;
7091 scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
7092 }
7093 if (window->ScrollTarget.y < FLT_MAX)
7094 {
7095 // '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.
7096 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7097 float cr_y = window->ScrollTargetCenterRatio.y;
7098 float target_y = window->ScrollTarget.y;
7099 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
7100 target_y = 0.0f;
7101 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y)
7102 target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f;
7103 scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height);
7104 }
7105 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
7106 if (!window->Collapsed && !window->SkipItems)
7107 {
7108 scroll.x = ImMin(scroll.x, window->ScrollMax.x);
7109 scroll.y = ImMin(scroll.y, window->ScrollMax.y);
7110 }
7111 return scroll;
7112 }
7113
7114 // Scroll to keep newly navigated item fully into view
ScrollToBringRectIntoView(ImGuiWindow * window,const ImRect & item_rect)7115 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect)
7116 {
7117 ImGuiContext& g = *GImGui;
7118 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
7119 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7120
7121 ImVec2 delta_scroll;
7122 if (!window_rect.Contains(item_rect))
7123 {
7124 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7125 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f);
7126 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7127 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f);
7128 if (item_rect.Min.y < window_rect.Min.y)
7129 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f);
7130 else if (item_rect.Max.y >= window_rect.Max.y)
7131 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f);
7132
7133 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false);
7134 delta_scroll = next_scroll - window->Scroll;
7135 }
7136
7137 // Also scroll parent window to keep us into view if necessary
7138 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7139 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll));
7140
7141 return delta_scroll;
7142 }
7143
GetScrollX()7144 float ImGui::GetScrollX()
7145 {
7146 ImGuiWindow* window = GImGui->CurrentWindow;
7147 return window->Scroll.x;
7148 }
7149
GetScrollY()7150 float ImGui::GetScrollY()
7151 {
7152 ImGuiWindow* window = GImGui->CurrentWindow;
7153 return window->Scroll.y;
7154 }
7155
GetScrollMaxX()7156 float ImGui::GetScrollMaxX()
7157 {
7158 ImGuiWindow* window = GImGui->CurrentWindow;
7159 return window->ScrollMax.x;
7160 }
7161
GetScrollMaxY()7162 float ImGui::GetScrollMaxY()
7163 {
7164 ImGuiWindow* window = GImGui->CurrentWindow;
7165 return window->ScrollMax.y;
7166 }
7167
SetScrollX(float scroll_x)7168 void ImGui::SetScrollX(float scroll_x)
7169 {
7170 ImGuiWindow* window = GetCurrentWindow();
7171 window->ScrollTarget.x = scroll_x;
7172 window->ScrollTargetCenterRatio.x = 0.0f;
7173 }
7174
SetScrollY(float scroll_y)7175 void ImGui::SetScrollY(float scroll_y)
7176 {
7177 ImGuiWindow* window = GetCurrentWindow();
7178 window->ScrollTarget.y = scroll_y;
7179 window->ScrollTargetCenterRatio.y = 0.0f;
7180 }
7181
SetScrollX(ImGuiWindow * window,float new_scroll_x)7182 void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
7183 {
7184 window->ScrollTarget.x = new_scroll_x;
7185 window->ScrollTargetCenterRatio.x = 0.0f;
7186 }
7187
SetScrollY(ImGuiWindow * window,float new_scroll_y)7188 void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
7189 {
7190 window->ScrollTarget.y = new_scroll_y;
7191 window->ScrollTargetCenterRatio.y = 0.0f;
7192 }
7193
7194
SetScrollFromPosX(ImGuiWindow * window,float local_x,float center_x_ratio)7195 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
7196 {
7197 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7198 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
7199 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x);
7200 window->ScrollTargetCenterRatio.x = center_x_ratio;
7201 }
7202
SetScrollFromPosY(ImGuiWindow * window,float local_y,float center_y_ratio)7203 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
7204 {
7205 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
7206 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
7207 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight();
7208 local_y -= decoration_up_height;
7209 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y);
7210 window->ScrollTargetCenterRatio.y = center_y_ratio;
7211 }
7212
SetScrollFromPosX(float local_x,float center_x_ratio)7213 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
7214 {
7215 ImGuiContext& g = *GImGui;
7216 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
7217 }
7218
SetScrollFromPosY(float local_y,float center_y_ratio)7219 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
7220 {
7221 ImGuiContext& g = *GImGui;
7222 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
7223 }
7224
7225 // 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)7226 void ImGui::SetScrollHereX(float center_x_ratio)
7227 {
7228 ImGuiContext& g = *GImGui;
7229 ImGuiWindow* window = g.CurrentWindow;
7230 float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space
7231 float last_item_width = window->DC.LastItemRect.GetWidth();
7232 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.
7233 SetScrollFromPosX(target_x, center_x_ratio);
7234 }
7235
7236 // 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)7237 void ImGui::SetScrollHereY(float center_y_ratio)
7238 {
7239 ImGuiContext& g = *GImGui;
7240 ImGuiWindow* window = g.CurrentWindow;
7241 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
7242 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.
7243 SetScrollFromPosY(target_y, center_y_ratio);
7244 }
7245
7246 //-----------------------------------------------------------------------------
7247 // [SECTION] TOOLTIPS
7248 //-----------------------------------------------------------------------------
7249
BeginTooltip()7250 void ImGui::BeginTooltip()
7251 {
7252 ImGuiContext& g = *GImGui;
7253 if (g.DragDropWithinSourceOrTarget)
7254 {
7255 // 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)
7256 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
7257 // 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.
7258 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
7259 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
7260 SetNextWindowPos(tooltip_pos);
7261 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
7262 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
7263 BeginTooltipEx(0, true);
7264 }
7265 else
7266 {
7267 BeginTooltipEx(0, false);
7268 }
7269 }
7270
7271 // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first.
BeginTooltipEx(ImGuiWindowFlags extra_flags,bool override_previous_tooltip)7272 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
7273 {
7274 ImGuiContext& g = *GImGui;
7275 char window_name[16];
7276 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
7277 if (override_previous_tooltip)
7278 if (ImGuiWindow* window = FindWindowByName(window_name))
7279 if (window->Active)
7280 {
7281 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
7282 window->Hidden = true;
7283 window->HiddenFramesCanSkipItems = 1;
7284 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
7285 }
7286 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
7287 Begin(window_name, NULL, flags | extra_flags);
7288 }
7289
EndTooltip()7290 void ImGui::EndTooltip()
7291 {
7292 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
7293 End();
7294 }
7295
SetTooltipV(const char * fmt,va_list args)7296 void ImGui::SetTooltipV(const char* fmt, va_list args)
7297 {
7298 ImGuiContext& g = *GImGui;
7299 if (g.DragDropWithinSourceOrTarget)
7300 BeginTooltip();
7301 else
7302 BeginTooltipEx(0, true);
7303 TextV(fmt, args);
7304 EndTooltip();
7305 }
7306
SetTooltip(const char * fmt,...)7307 void ImGui::SetTooltip(const char* fmt, ...)
7308 {
7309 va_list args;
7310 va_start(args, fmt);
7311 SetTooltipV(fmt, args);
7312 va_end(args);
7313 }
7314
7315 //-----------------------------------------------------------------------------
7316 // [SECTION] POPUPS
7317 //-----------------------------------------------------------------------------
7318
IsPopupOpen(ImGuiID id)7319 bool ImGui::IsPopupOpen(ImGuiID id)
7320 {
7321 ImGuiContext& g = *GImGui;
7322 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
7323 }
7324
IsPopupOpen(const char * str_id)7325 bool ImGui::IsPopupOpen(const char* str_id)
7326 {
7327 ImGuiContext& g = *GImGui;
7328 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
7329 }
7330
GetTopMostPopupModal()7331 ImGuiWindow* ImGui::GetTopMostPopupModal()
7332 {
7333 ImGuiContext& g = *GImGui;
7334 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
7335 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
7336 if (popup->Flags & ImGuiWindowFlags_Modal)
7337 return popup;
7338 return NULL;
7339 }
7340
OpenPopup(const char * str_id)7341 void ImGui::OpenPopup(const char* str_id)
7342 {
7343 ImGuiContext& g = *GImGui;
7344 OpenPopupEx(g.CurrentWindow->GetID(str_id));
7345 }
7346
7347 // Mark popup as open (toggle toward open state).
7348 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
7349 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
7350 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)7351 void ImGui::OpenPopupEx(ImGuiID id)
7352 {
7353 ImGuiContext& g = *GImGui;
7354 ImGuiWindow* parent_window = g.CurrentWindow;
7355 int current_stack_size = g.BeginPopupStack.Size;
7356 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
7357 popup_ref.PopupId = id;
7358 popup_ref.Window = NULL;
7359 popup_ref.SourceWindow = g.NavWindow;
7360 popup_ref.OpenFrameCount = g.FrameCount;
7361 popup_ref.OpenParentId = parent_window->IDStack.back();
7362 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
7363 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
7364
7365 //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
7366 if (g.OpenPopupStack.Size < current_stack_size + 1)
7367 {
7368 g.OpenPopupStack.push_back(popup_ref);
7369 }
7370 else
7371 {
7372 // 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
7373 // 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
7374 // 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.
7375 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
7376 {
7377 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
7378 }
7379 else
7380 {
7381 // Close child popups if any, then flag popup for open/reopen
7382 g.OpenPopupStack.resize(current_stack_size + 1);
7383 g.OpenPopupStack[current_stack_size] = popup_ref;
7384 }
7385
7386 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
7387 // This is equivalent to what ClosePopupToLevel() does.
7388 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
7389 // FocusWindow(parent_window);
7390 }
7391 }
7392
ClosePopupsOverWindow(ImGuiWindow * ref_window,bool restore_focus_to_window_under_popup)7393 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
7394 {
7395 ImGuiContext& g = *GImGui;
7396 if (g.OpenPopupStack.empty())
7397 return;
7398
7399 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
7400 // Don't close our own child popup windows.
7401 int popup_count_to_keep = 0;
7402 if (ref_window)
7403 {
7404 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
7405 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
7406 {
7407 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
7408 if (!popup.Window)
7409 continue;
7410 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
7411 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
7412 continue;
7413
7414 // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow)
7415 bool popup_or_descendent_is_ref_window = false;
7416 for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++)
7417 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window)
7418 if (popup_window->RootWindow == ref_window->RootWindow)
7419 popup_or_descendent_is_ref_window = true;
7420 if (!popup_or_descendent_is_ref_window)
7421 break;
7422 }
7423 }
7424 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
7425 {
7426 //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
7427 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
7428 }
7429 }
7430
ClosePopupToLevel(int remaining,bool restore_focus_to_window_under_popup)7431 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
7432 {
7433 ImGuiContext& g = *GImGui;
7434 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
7435 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow;
7436 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;
7437 g.OpenPopupStack.resize(remaining);
7438
7439 if (restore_focus_to_window_under_popup)
7440 {
7441 if (focus_window && !focus_window->WasActive && popup_window)
7442 {
7443 // Fallback
7444 FocusTopMostWindowUnderOne(popup_window, NULL);
7445 }
7446 else
7447 {
7448 if (g.NavLayer == 0 && focus_window)
7449 focus_window = NavRestoreLastChildNavWindow(focus_window);
7450 FocusWindow(focus_window);
7451 }
7452 }
7453 }
7454
7455 // Close the popup we have begin-ed into.
CloseCurrentPopup()7456 void ImGui::CloseCurrentPopup()
7457 {
7458 ImGuiContext& g = *GImGui;
7459 int popup_idx = g.BeginPopupStack.Size - 1;
7460 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
7461 return;
7462
7463 // Closing a menu closes its top-most parent popup (unless a modal)
7464 while (popup_idx > 0)
7465 {
7466 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
7467 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
7468 bool close_parent = false;
7469 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
7470 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal))
7471 close_parent = true;
7472 if (!close_parent)
7473 break;
7474 popup_idx--;
7475 }
7476 //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
7477 ClosePopupToLevel(popup_idx, true);
7478
7479 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
7480 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
7481 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
7482 if (ImGuiWindow* window = g.NavWindow)
7483 window->DC.NavHideHighlightOneFrame = true;
7484 }
7485
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)7486 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
7487 {
7488 ImGuiContext& g = *GImGui;
7489 if (!IsPopupOpen(id))
7490 {
7491 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7492 return false;
7493 }
7494
7495 char name[20];
7496 if (extra_flags & ImGuiWindowFlags_ChildMenu)
7497 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
7498 else
7499 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
7500
7501 bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
7502 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
7503 EndPopup();
7504
7505 return is_open;
7506 }
7507
BeginPopup(const char * str_id,ImGuiWindowFlags flags)7508 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
7509 {
7510 ImGuiContext& g = *GImGui;
7511 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
7512 {
7513 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7514 return false;
7515 }
7516 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
7517 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
7518 }
7519
7520 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
7521 // 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)7522 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
7523 {
7524 ImGuiContext& g = *GImGui;
7525 ImGuiWindow* window = g.CurrentWindow;
7526 const ImGuiID id = window->GetID(name);
7527 if (!IsPopupOpen(id))
7528 {
7529 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
7530 return false;
7531 }
7532
7533 // Center modal windows by default
7534 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
7535 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)
7536 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
7537
7538 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
7539 const bool is_open = Begin(name, p_open, flags);
7540 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
7541 {
7542 EndPopup();
7543 if (is_open)
7544 ClosePopupToLevel(g.BeginPopupStack.Size, true);
7545 return false;
7546 }
7547 return is_open;
7548 }
7549
EndPopup()7550 void ImGui::EndPopup()
7551 {
7552 ImGuiContext& g = *GImGui;
7553 ImGuiWindow* window = g.CurrentWindow;
7554 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
7555 IM_ASSERT(g.BeginPopupStack.Size > 0);
7556
7557 // Make all menus and popups wrap around for now, may need to expose that policy.
7558 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
7559
7560 // Child-popups don't need to be layed out
7561 IM_ASSERT(g.WithinEndChild == false);
7562 if (window->Flags & ImGuiWindowFlags_ChildWindow)
7563 g.WithinEndChild = true;
7564 End();
7565 g.WithinEndChild = false;
7566 }
7567
OpenPopupOnItemClick(const char * str_id,int mouse_button)7568 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
7569 {
7570 ImGuiWindow* window = GImGui->CurrentWindow;
7571 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7572 {
7573 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!
7574 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7575 OpenPopupEx(id);
7576 return true;
7577 }
7578 return false;
7579 }
7580
7581 // This is a helper to handle the simplest case of associating one named popup to one given widget.
7582 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
7583 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)7584 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
7585 {
7586 ImGuiWindow* window = GImGui->CurrentWindow;
7587 if (window->SkipItems)
7588 return false;
7589 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!
7590 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
7591 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7592 OpenPopupEx(id);
7593 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7594 }
7595
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)7596 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
7597 {
7598 if (!str_id)
7599 str_id = "window_context";
7600 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7601 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
7602 if (also_over_items || !IsAnyItemHovered())
7603 OpenPopupEx(id);
7604 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7605 }
7606
BeginPopupContextVoid(const char * str_id,int mouse_button)7607 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
7608 {
7609 if (!str_id)
7610 str_id = "void_context";
7611 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
7612 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
7613 OpenPopupEx(id);
7614 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
7615 }
7616
7617 // 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.)
7618 // 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)7619 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
7620 {
7621 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
7622 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
7623 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
7624
7625 // Combo Box policy (we want a connecting edge)
7626 if (policy == ImGuiPopupPositionPolicy_ComboBox)
7627 {
7628 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
7629 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7630 {
7631 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7632 if (n != -1 && dir == *last_dir) // Already tried this direction?
7633 continue;
7634 ImVec2 pos;
7635 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
7636 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
7637 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
7638 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
7639 if (!r_outer.Contains(ImRect(pos, pos + size)))
7640 continue;
7641 *last_dir = dir;
7642 return pos;
7643 }
7644 }
7645
7646 // Default popup policy
7647 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
7648 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
7649 {
7650 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
7651 if (n != -1 && dir == *last_dir) // Already tried this direction?
7652 continue;
7653 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);
7654 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);
7655 if (avail_w < size.x || avail_h < size.y)
7656 continue;
7657 ImVec2 pos;
7658 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
7659 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
7660 *last_dir = dir;
7661 return pos;
7662 }
7663
7664 // Fallback, try to keep within display
7665 *last_dir = ImGuiDir_None;
7666 ImVec2 pos = ref_pos;
7667 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
7668 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
7669 return pos;
7670 }
7671
GetWindowAllowedExtentRect(ImGuiWindow * window)7672 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window)
7673 {
7674 IM_UNUSED(window);
7675 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
7676 ImRect r_screen = GetViewportRect();
7677 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
7678 return r_screen;
7679 }
7680
FindBestWindowPosForPopup(ImGuiWindow * window)7681 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
7682 {
7683 ImGuiContext& g = *GImGui;
7684
7685 ImRect r_outer = GetWindowAllowedExtentRect(window);
7686 if (window->Flags & ImGuiWindowFlags_ChildMenu)
7687 {
7688 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
7689 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
7690 IM_ASSERT(g.CurrentWindow == window);
7691 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
7692 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).
7693 ImRect r_avoid;
7694 if (parent_window->DC.MenuBarAppending)
7695 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
7696 else
7697 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);
7698 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7699 }
7700 if (window->Flags & ImGuiWindowFlags_Popup)
7701 {
7702 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
7703 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7704 }
7705 if (window->Flags & ImGuiWindowFlags_Tooltip)
7706 {
7707 // Position tooltip (always follows mouse)
7708 float sc = g.Style.MouseCursorScale;
7709 ImVec2 ref_pos = NavCalcPreferredRefPos();
7710 ImRect r_avoid;
7711 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
7712 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
7713 else
7714 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.
7715 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
7716 if (window->AutoPosLastDirection == ImGuiDir_None)
7717 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.
7718 return pos;
7719 }
7720 IM_ASSERT(0);
7721 return window->Pos;
7722 }
7723
7724
7725 //-----------------------------------------------------------------------------
7726 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
7727 //-----------------------------------------------------------------------------
7728
ImGetDirQuadrantFromDelta(float dx,float dy)7729 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
7730 {
7731 if (ImFabs(dx) > ImFabs(dy))
7732 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
7733 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
7734 }
7735
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)7736 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
7737 {
7738 if (a1 < b0)
7739 return a1 - b0;
7740 if (b1 < a0)
7741 return a0 - b1;
7742 return 0.0f;
7743 }
7744
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7745 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7746 {
7747 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7748 {
7749 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7750 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7751 }
7752 else
7753 {
7754 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7755 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7756 }
7757 }
7758
7759 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7760 static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7761 {
7762 ImGuiContext& g = *GImGui;
7763 ImGuiWindow* window = g.CurrentWindow;
7764 if (g.NavLayer != window->DC.NavLayerCurrent)
7765 return false;
7766
7767 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)
7768 g.NavScoringCount++;
7769
7770 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7771 if (window->ParentWindow == g.NavWindow)
7772 {
7773 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7774 if (!window->ClipRect.Overlaps(cand))
7775 return false;
7776 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7777 }
7778
7779 // 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)
7780 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7781 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7782
7783 // Compute distance between boxes
7784 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7785 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7786 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
7787 if (dby != 0.0f && dbx != 0.0f)
7788 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7789 float dist_box = ImFabs(dbx) + ImFabs(dby);
7790
7791 // 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)
7792 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7793 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7794 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7795
7796 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7797 ImGuiDir quadrant;
7798 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7799 if (dbx != 0.0f || dby != 0.0f)
7800 {
7801 // For non-overlapping boxes, use distance between boxes
7802 dax = dbx;
7803 day = dby;
7804 dist_axial = dist_box;
7805 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7806 }
7807 else if (dcx != 0.0f || dcy != 0.0f)
7808 {
7809 // For overlapping boxes with different centers, use distance between centers
7810 dax = dcx;
7811 day = dcy;
7812 dist_axial = dist_center;
7813 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7814 }
7815 else
7816 {
7817 // 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)
7818 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7819 }
7820
7821 #if IMGUI_DEBUG_NAV_SCORING
7822 char buf[128];
7823 if (IsMouseHoveringRect(cand.Min, cand.Max))
7824 {
7825 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]);
7826 ImDrawList* draw_list = GetForegroundDrawList(window);
7827 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7828 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7829 draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150));
7830 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7831 }
7832 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7833 {
7834 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7835 if (quadrant == g.NavMoveDir)
7836 {
7837 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7838 ImDrawList* draw_list = GetForegroundDrawList(window);
7839 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7840 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7841 }
7842 }
7843 #endif
7844
7845 // Is it in the quadrant we're interesting in moving to?
7846 bool new_best = false;
7847 if (quadrant == g.NavMoveDir)
7848 {
7849 // Does it beat the current best candidate?
7850 if (dist_box < result->DistBox)
7851 {
7852 result->DistBox = dist_box;
7853 result->DistCenter = dist_center;
7854 return true;
7855 }
7856 if (dist_box == result->DistBox)
7857 {
7858 // Try using distance between center points to break ties
7859 if (dist_center < result->DistCenter)
7860 {
7861 result->DistCenter = dist_center;
7862 new_best = true;
7863 }
7864 else if (dist_center == result->DistCenter)
7865 {
7866 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7867 // (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),
7868 // 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.
7869 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7870 new_best = true;
7871 }
7872 }
7873 }
7874
7875 // 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
7876 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7877 // 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.
7878 // 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.
7879 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7880 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
7881 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7882 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))
7883 {
7884 result->DistAxial = dist_axial;
7885 new_best = true;
7886 }
7887
7888 return new_best;
7889 }
7890
7891 // 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)7892 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7893 {
7894 ImGuiContext& g = *GImGui;
7895 //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.
7896 // return;
7897
7898 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7899 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7900
7901 // Process Init Request
7902 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7903 {
7904 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7905 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7906 {
7907 g.NavInitResultId = id;
7908 g.NavInitResultRectRel = nav_bb_rel;
7909 }
7910 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7911 {
7912 g.NavInitRequest = false; // Found a match, clear request
7913 NavUpdateAnyRequestFlag();
7914 }
7915 }
7916
7917 // Process Move Request (scoring for navigation)
7918 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7919 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav)))
7920 {
7921 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7922 #if IMGUI_DEBUG_NAV_SCORING
7923 // [DEBUG] Score all items in NavWindow at all times
7924 if (!g.NavMoveRequest)
7925 g.NavMoveDir = g.NavMoveDirLast;
7926 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7927 #else
7928 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7929 #endif
7930 if (new_best)
7931 {
7932 result->ID = id;
7933 result->SelectScopeId = g.MultiSelectScopeId;
7934 result->Window = window;
7935 result->RectRel = nav_bb_rel;
7936 }
7937
7938 const float VISIBLE_RATIO = 0.70f;
7939 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7940 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)
7941 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7942 {
7943 result = &g.NavMoveResultLocalVisibleSet;
7944 result->ID = id;
7945 result->SelectScopeId = g.MultiSelectScopeId;
7946 result->Window = window;
7947 result->RectRel = nav_bb_rel;
7948 }
7949 }
7950
7951 // Update window-relative bounding box of navigated item
7952 if (g.NavId == id)
7953 {
7954 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7955 g.NavLayer = window->DC.NavLayerCurrent;
7956 g.NavIdIsAlive = true;
7957 g.NavIdTabCounter = window->DC.FocusCounterTab;
7958 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
7959 }
7960 }
7961
NavMoveRequestButNoResultYet()7962 bool ImGui::NavMoveRequestButNoResultYet()
7963 {
7964 ImGuiContext& g = *GImGui;
7965 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7966 }
7967
NavMoveRequestCancel()7968 void ImGui::NavMoveRequestCancel()
7969 {
7970 ImGuiContext& g = *GImGui;
7971 g.NavMoveRequest = false;
7972 NavUpdateAnyRequestFlag();
7973 }
7974
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7975 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7976 {
7977 ImGuiContext& g = *GImGui;
7978 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7979 NavMoveRequestCancel();
7980 g.NavMoveDir = move_dir;
7981 g.NavMoveClipDir = clip_dir;
7982 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7983 g.NavMoveRequestFlags = move_flags;
7984 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7985 }
7986
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7987 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7988 {
7989 ImGuiContext& g = *GImGui;
7990 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7991 return;
7992 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7993 ImRect bb_rel = window->NavRectRel[0];
7994
7995 ImGuiDir clip_dir = g.NavMoveDir;
7996 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7997 {
7998 bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x;
7999 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
8000 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8001 }
8002 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
8003 {
8004 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
8005 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
8006 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8007 }
8008 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8009 {
8010 bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y;
8011 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
8012 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8013 }
8014 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
8015 {
8016 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
8017 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
8018 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
8019 }
8020 }
8021
8022 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
8023 // This way we could find the last focused window among our children. It would be much less confusing this way?
NavSaveLastChildNavWindowIntoParent(ImGuiWindow * nav_window)8024 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
8025 {
8026 ImGuiWindow* parent_window = nav_window;
8027 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8028 parent_window = parent_window->ParentWindow;
8029 if (parent_window && parent_window != nav_window)
8030 parent_window->NavLastChildNavWindow = nav_window;
8031 }
8032
8033 // Restore the last focused child.
8034 // Call when we are expected to land on the Main Layer (0) after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)8035 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
8036 {
8037 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
8038 }
8039
NavRestoreLayer(ImGuiNavLayer layer)8040 static void NavRestoreLayer(ImGuiNavLayer layer)
8041 {
8042 ImGuiContext& g = *GImGui;
8043 g.NavLayer = layer;
8044 if (layer == 0)
8045 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
8046 if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
8047 ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
8048 else
8049 ImGui::NavInitWindow(g.NavWindow, true);
8050 }
8051
NavUpdateAnyRequestFlag()8052 static inline void ImGui::NavUpdateAnyRequestFlag()
8053 {
8054 ImGuiContext& g = *GImGui;
8055 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
8056 if (g.NavAnyRequest)
8057 IM_ASSERT(g.NavWindow != NULL);
8058 }
8059
8060 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)8061 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
8062 {
8063 ImGuiContext& g = *GImGui;
8064 IM_ASSERT(window == g.NavWindow);
8065 bool init_for_nav = false;
8066 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
8067 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
8068 init_for_nav = true;
8069 //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
8070 if (init_for_nav)
8071 {
8072 SetNavID(0, g.NavLayer);
8073 g.NavInitRequest = true;
8074 g.NavInitRequestFromMove = false;
8075 g.NavInitResultId = 0;
8076 g.NavInitResultRectRel = ImRect();
8077 NavUpdateAnyRequestFlag();
8078 }
8079 else
8080 {
8081 g.NavId = window->NavLastIds[0];
8082 }
8083 }
8084
NavCalcPreferredRefPos()8085 static ImVec2 ImGui::NavCalcPreferredRefPos()
8086 {
8087 ImGuiContext& g = *GImGui;
8088 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
8089 {
8090 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
8091 if (IsMousePosValid(&g.IO.MousePos))
8092 return g.IO.MousePos;
8093 return g.LastValidMousePos;
8094 }
8095 else
8096 {
8097 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
8098 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
8099 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()));
8100 ImRect visible_rect = GetViewportRect();
8101 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.
8102 }
8103 }
8104
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)8105 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
8106 {
8107 ImGuiContext& g = *GImGui;
8108 if (mode == ImGuiInputReadMode_Down)
8109 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
8110
8111 const float t = g.IO.NavInputsDownDuration[n];
8112 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
8113 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
8114 if (t < 0.0f)
8115 return 0.0f;
8116 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
8117 return (t == 0.0f) ? 1.0f : 0.0f;
8118 if (mode == ImGuiInputReadMode_Repeat)
8119 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f);
8120 if (mode == ImGuiInputReadMode_RepeatSlow)
8121 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f);
8122 if (mode == ImGuiInputReadMode_RepeatFast)
8123 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f);
8124 return 0.0f;
8125 }
8126
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)8127 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
8128 {
8129 ImVec2 delta(0.0f, 0.0f);
8130 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
8131 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
8132 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
8133 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
8134 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
8135 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
8136 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
8137 delta *= slow_factor;
8138 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
8139 delta *= fast_factor;
8140 return delta;
8141 }
8142
NavUpdate()8143 static void ImGui::NavUpdate()
8144 {
8145 ImGuiContext& g = *GImGui;
8146 g.IO.WantSetMousePos = false;
8147 #if 0
8148 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);
8149 #endif
8150
8151 // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
8152 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
8153 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
8154 if (nav_gamepad_active)
8155 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)
8156 g.NavInputSource = ImGuiInputSource_NavGamepad;
8157
8158 // Update Keyboard->Nav inputs mapping
8159 if (nav_keyboard_active)
8160 {
8161 #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)
8162 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
8163 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
8164 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
8165 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
8166 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
8167 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
8168 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
8169 if (g.IO.KeyCtrl)
8170 g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
8171 if (g.IO.KeyShift)
8172 g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
8173 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.
8174 g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
8175 #undef NAV_MAP_KEY
8176 }
8177 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
8178 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
8179 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;
8180
8181 // Process navigation init request (select first/default focus)
8182 // 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)
8183 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow)
8184 {
8185 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
8186 //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name);
8187 if (g.NavInitRequestFromMove)
8188 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
8189 else
8190 SetNavID(g.NavInitResultId, g.NavLayer);
8191 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
8192 }
8193 g.NavInitRequest = false;
8194 g.NavInitRequestFromMove = false;
8195 g.NavInitResultId = 0;
8196 g.NavJustMovedToId = 0;
8197
8198 // Process navigation move request
8199 if (g.NavMoveRequest)
8200 NavUpdateMoveResult();
8201
8202 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
8203 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
8204 {
8205 IM_ASSERT(g.NavMoveRequest);
8206 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8207 g.NavDisableHighlight = false;
8208 g.NavMoveRequestForward = ImGuiNavForward_None;
8209 }
8210
8211 // Apply application mouse position movement, after we had a chance to process move request result.
8212 if (g.NavMousePosDirty && g.NavIdIsAlive)
8213 {
8214 // Set mouse position given our knowledge of the navigated item position from last frame
8215 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
8216 {
8217 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
8218 {
8219 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
8220 g.IO.WantSetMousePos = true;
8221 }
8222 }
8223 g.NavMousePosDirty = false;
8224 }
8225 g.NavIdIsAlive = false;
8226 g.NavJustTabbedId = 0;
8227 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
8228
8229 // 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
8230 if (g.NavWindow)
8231 NavSaveLastChildNavWindowIntoParent(g.NavWindow);
8232 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
8233 g.NavWindow->NavLastChildNavWindow = NULL;
8234
8235 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
8236 NavUpdateWindowing();
8237
8238 // Set output flags for user application
8239 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
8240 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);
8241
8242 // Process NavCancel input (to close a popup, get back to parent, clear focus)
8243 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
8244 {
8245 if (g.ActiveId != 0)
8246 {
8247 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel))
8248 ClearActiveID();
8249 }
8250 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
8251 {
8252 // Exit child window
8253 ImGuiWindow* child_window = g.NavWindow;
8254 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
8255 IM_ASSERT(child_window->ChildId != 0);
8256 FocusWindow(parent_window);
8257 SetNavID(child_window->ChildId, 0);
8258 g.NavIdIsAlive = false;
8259 if (g.NavDisableMouseHover)
8260 g.NavMousePosDirty = true;
8261 }
8262 else if (g.OpenPopupStack.Size > 0)
8263 {
8264 // Close open popup/menu
8265 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
8266 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
8267 }
8268 else if (g.NavLayer != 0)
8269 {
8270 // Leave the "menu" layer
8271 NavRestoreLayer(ImGuiNavLayer_Main);
8272 }
8273 else
8274 {
8275 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
8276 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
8277 g.NavWindow->NavLastIds[0] = 0;
8278 g.NavId = 0;
8279 }
8280 }
8281
8282 // Process manual activation request
8283 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
8284 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8285 {
8286 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
8287 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
8288 if (g.ActiveId == 0 && activate_pressed)
8289 g.NavActivateId = g.NavId;
8290 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
8291 g.NavActivateDownId = g.NavId;
8292 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
8293 g.NavActivatePressedId = g.NavId;
8294 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
8295 g.NavInputId = g.NavId;
8296 }
8297 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8298 g.NavDisableHighlight = true;
8299 if (g.NavActivateId != 0)
8300 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
8301 g.NavMoveRequest = false;
8302
8303 // Process programmatic activation request
8304 if (g.NavNextActivateId != 0)
8305 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
8306 g.NavNextActivateId = 0;
8307
8308 // Initiate directional inputs request
8309 if (g.NavMoveRequestForward == ImGuiNavForward_None)
8310 {
8311 g.NavMoveDir = ImGuiDir_None;
8312 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
8313 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
8314 {
8315 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat;
8316 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; }
8317 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; }
8318 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; }
8319 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
8320 }
8321 g.NavMoveClipDir = g.NavMoveDir;
8322 }
8323 else
8324 {
8325 // 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)
8326 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
8327 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
8328 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
8329 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
8330 }
8331
8332 // Update PageUp/PageDown/Home/End scroll
8333 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
8334 float nav_scoring_rect_offset_y = 0.0f;
8335 if (nav_keyboard_active)
8336 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown();
8337
8338 // 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
8339 if (g.NavMoveDir != ImGuiDir_None)
8340 {
8341 g.NavMoveRequest = true;
8342 g.NavMoveDirLast = g.NavMoveDir;
8343 }
8344 if (g.NavMoveRequest && g.NavId == 0)
8345 {
8346 //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer);
8347 g.NavInitRequest = g.NavInitRequestFromMove = true;
8348 g.NavInitResultId = 0;
8349 g.NavDisableHighlight = false;
8350 }
8351 NavUpdateAnyRequestFlag();
8352
8353 // Scrolling
8354 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
8355 {
8356 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
8357 ImGuiWindow* window = g.NavWindow;
8358 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.
8359 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
8360 {
8361 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
8362 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
8363 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
8364 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
8365 }
8366
8367 // *Normal* Manual scroll with NavScrollXXX keys
8368 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
8369 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
8370 if (scroll_dir.x != 0.0f && window->ScrollbarX)
8371 {
8372 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
8373 g.NavMoveFromClampedRefRect = true;
8374 }
8375 if (scroll_dir.y != 0.0f)
8376 {
8377 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
8378 g.NavMoveFromClampedRefRect = true;
8379 }
8380 }
8381
8382 // Reset search results
8383 g.NavMoveResultLocal.Clear();
8384 g.NavMoveResultLocalVisibleSet.Clear();
8385 g.NavMoveResultOther.Clear();
8386
8387 // 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
8388 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
8389 {
8390 ImGuiWindow* window = g.NavWindow;
8391 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1));
8392 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
8393 {
8394 float pad = window->CalcFontSize() * 0.5f;
8395 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
8396 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
8397 g.NavId = 0;
8398 }
8399 g.NavMoveFromClampedRefRect = false;
8400 }
8401
8402 // 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)
8403 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
8404 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
8405 g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
8406 g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
8407 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
8408 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().
8409 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
8410 g.NavScoringCount = 0;
8411 #if IMGUI_DEBUG_NAV_RECTS
8412 if (g.NavWindow)
8413 {
8414 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow);
8415 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]
8416 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); }
8417 }
8418 #endif
8419 }
8420
8421 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()8422 static void ImGui::NavUpdateMoveResult()
8423 {
8424 ImGuiContext& g = *GImGui;
8425 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
8426 {
8427 // 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)
8428 if (g.NavId != 0)
8429 {
8430 g.NavDisableHighlight = false;
8431 g.NavDisableMouseHover = true;
8432 }
8433 return;
8434 }
8435
8436 // Select which result to use
8437 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
8438
8439 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
8440 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
8441 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
8442 result = &g.NavMoveResultLocalVisibleSet;
8443
8444 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
8445 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
8446 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
8447 result = &g.NavMoveResultOther;
8448 IM_ASSERT(g.NavWindow && result->Window);
8449
8450 // Scroll to keep newly navigated item fully into view.
8451 if (g.NavLayer == 0)
8452 {
8453 ImVec2 delta_scroll;
8454 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge)
8455 {
8456 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
8457 delta_scroll.y = result->Window->Scroll.y - scroll_target;
8458 SetScrollY(result->Window, scroll_target);
8459 }
8460 else
8461 {
8462 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
8463 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs);
8464 }
8465
8466 // Offset our result position so mouse position can be applied immediately after in NavUpdate()
8467 result->RectRel.TranslateX(-delta_scroll.x);
8468 result->RectRel.TranslateY(-delta_scroll.y);
8469 }
8470
8471 ClearActiveID();
8472 g.NavWindow = result->Window;
8473 if (g.NavId != result->ID)
8474 {
8475 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
8476 g.NavJustMovedToId = result->ID;
8477 g.NavJustMovedToMultiSelectScopeId = result->SelectScopeId;
8478 }
8479 SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
8480 g.NavMoveFromClampedRefRect = false;
8481 }
8482
8483 // Handle PageUp/PageDown/Home/End keys
NavUpdatePageUpPageDown()8484 static float ImGui::NavUpdatePageUpPageDown()
8485 {
8486 ImGuiContext& g = *GImGui;
8487 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL)
8488 return 0.0f;
8489 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != 0)
8490 return 0.0f;
8491
8492 ImGuiWindow* window = g.NavWindow;
8493 const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
8494 const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
8495 const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
8496 const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
8497 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed
8498 {
8499 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
8500 {
8501 // Fallback manual-scroll when window has no navigable item
8502 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8503 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
8504 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8505 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
8506 else if (home_pressed)
8507 SetScrollY(window, 0.0f);
8508 else if (end_pressed)
8509 SetScrollY(window, window->ScrollMax.y);
8510 }
8511 else
8512 {
8513 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
8514 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
8515 float nav_scoring_rect_offset_y = 0.0f;
8516 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
8517 {
8518 nav_scoring_rect_offset_y = -page_offset_y;
8519 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)
8520 g.NavMoveClipDir = ImGuiDir_Up;
8521 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8522 }
8523 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
8524 {
8525 nav_scoring_rect_offset_y = +page_offset_y;
8526 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)
8527 g.NavMoveClipDir = ImGuiDir_Down;
8528 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
8529 }
8530 else if (home_pressed)
8531 {
8532 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
8533 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result.
8534 // Preserve current horizontal position if we have any.
8535 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y;
8536 if (nav_rect_rel.IsInverted())
8537 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8538 g.NavMoveDir = ImGuiDir_Down;
8539 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8540 }
8541 else if (end_pressed)
8542 {
8543 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y;
8544 if (nav_rect_rel.IsInverted())
8545 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
8546 g.NavMoveDir = ImGuiDir_Up;
8547 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge;
8548 }
8549 return nav_scoring_rect_offset_y;
8550 }
8551 }
8552 return 0.0f;
8553 }
8554
FindWindowFocusIndex(ImGuiWindow * window)8555 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
8556 {
8557 ImGuiContext& g = *GImGui;
8558 for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
8559 if (g.WindowsFocusOrder[i] == window)
8560 return i;
8561 return -1;
8562 }
8563
FindWindowNavFocusable(int i_start,int i_stop,int dir)8564 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
8565 {
8566 ImGuiContext& g = *GImGui;
8567 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
8568 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
8569 return g.WindowsFocusOrder[i];
8570 return NULL;
8571 }
8572
NavUpdateWindowingHighlightWindow(int focus_change_dir)8573 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
8574 {
8575 ImGuiContext& g = *GImGui;
8576 IM_ASSERT(g.NavWindowingTarget);
8577 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
8578 return;
8579
8580 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
8581 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
8582 if (!window_target)
8583 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
8584 if (window_target) // Don't reset windowing target if there's a single window in the list
8585 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
8586 g.NavWindowingToggleLayer = false;
8587 }
8588
8589 // Windowing management mode
8590 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
8591 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
NavUpdateWindowing()8592 static void ImGui::NavUpdateWindowing()
8593 {
8594 ImGuiContext& g = *GImGui;
8595 ImGuiWindow* apply_focus_window = NULL;
8596 bool apply_toggle_layer = false;
8597
8598 ImGuiWindow* modal_window = GetTopMostPopupModal();
8599 if (modal_window != NULL)
8600 {
8601 g.NavWindowingTarget = NULL;
8602 return;
8603 }
8604
8605 // Fade out
8606 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
8607 {
8608 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
8609 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
8610 g.NavWindowingTargetAnim = NULL;
8611 }
8612
8613 // Start CTRL-TAB or Square+L/R window selection
8614 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
8615 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
8616 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
8617 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
8618 {
8619 g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
8620 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
8621 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
8622 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
8623 }
8624
8625 // Gamepad update
8626 g.NavWindowingTimer += g.IO.DeltaTime;
8627 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
8628 {
8629 // 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
8630 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
8631
8632 // Select window to focus
8633 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
8634 if (focus_change_dir != 0)
8635 {
8636 NavUpdateWindowingHighlightWindow(focus_change_dir);
8637 g.NavWindowingHighlightAlpha = 1.0f;
8638 }
8639
8640 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
8641 if (!IsNavInputDown(ImGuiNavInput_Menu))
8642 {
8643 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
8644 if (g.NavWindowingToggleLayer && g.NavWindow)
8645 apply_toggle_layer = true;
8646 else if (!g.NavWindowingToggleLayer)
8647 apply_focus_window = g.NavWindowingTarget;
8648 g.NavWindowingTarget = NULL;
8649 }
8650 }
8651
8652 // Keyboard: Focus
8653 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
8654 {
8655 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
8656 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
8657 if (IsKeyPressedMap(ImGuiKey_Tab, true))
8658 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
8659 if (!g.IO.KeyCtrl)
8660 apply_focus_window = g.NavWindowingTarget;
8661 }
8662
8663 // Keyboard: Press and Release ALT to toggle menu layer
8664 // 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
8665 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed))
8666 g.NavWindowingToggleLayer = true;
8667 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
8668 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
8669 apply_toggle_layer = true;
8670
8671 // Move window
8672 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
8673 {
8674 ImVec2 move_delta;
8675 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
8676 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
8677 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
8678 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
8679 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
8680 {
8681 const float NAV_MOVE_SPEED = 800.0f;
8682 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
8683 SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always);
8684 g.NavDisableMouseHover = true;
8685 MarkIniSettingsDirty(g.NavWindowingTarget);
8686 }
8687 }
8688
8689 // Apply final focus
8690 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
8691 {
8692 ClearActiveID();
8693 g.NavDisableHighlight = false;
8694 g.NavDisableMouseHover = true;
8695 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
8696 ClosePopupsOverWindow(apply_focus_window, false);
8697 FocusWindow(apply_focus_window);
8698 if (apply_focus_window->NavLastIds[0] == 0)
8699 NavInitWindow(apply_focus_window, false);
8700
8701 // If the window only has a menu layer, select it directly
8702 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
8703 g.NavLayer = ImGuiNavLayer_Menu;
8704 }
8705 if (apply_focus_window)
8706 g.NavWindowingTarget = NULL;
8707
8708 // Apply menu/layer toggle
8709 if (apply_toggle_layer && g.NavWindow)
8710 {
8711 // Move to parent menu if necessary
8712 ImGuiWindow* new_nav_window = g.NavWindow;
8713 while (new_nav_window->ParentWindow
8714 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
8715 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
8716 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
8717 new_nav_window = new_nav_window->ParentWindow;
8718 if (new_nav_window != g.NavWindow)
8719 {
8720 ImGuiWindow* old_nav_window = g.NavWindow;
8721 FocusWindow(new_nav_window);
8722 new_nav_window->NavLastChildNavWindow = old_nav_window;
8723 }
8724 g.NavDisableHighlight = false;
8725 g.NavDisableMouseHover = true;
8726
8727 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID.
8728 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
8729 NavRestoreLayer(new_nav_layer);
8730 }
8731 }
8732
8733 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)8734 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
8735 {
8736 if (window->Flags & ImGuiWindowFlags_Popup)
8737 return "(Popup)";
8738 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
8739 return "(Main menu bar)";
8740 return "(Untitled)";
8741 }
8742
8743 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingOverlay()8744 void ImGui::NavUpdateWindowingOverlay()
8745 {
8746 ImGuiContext& g = *GImGui;
8747 IM_ASSERT(g.NavWindowingTarget != NULL);
8748
8749 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
8750 return;
8751
8752 if (g.NavWindowingList == NULL)
8753 g.NavWindowingList = FindWindowByName("###NavWindowingList");
8754 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
8755 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
8756 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
8757 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
8758 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
8759 {
8760 ImGuiWindow* window = g.WindowsFocusOrder[n];
8761 if (!IsWindowNavFocusable(window))
8762 continue;
8763 const char* label = window->Name;
8764 if (label == FindRenderedTextEnd(label))
8765 label = GetFallbackWindowNameForWindowingList(window);
8766 Selectable(label, g.NavWindowingTarget == window);
8767 }
8768 End();
8769 PopStyleVar();
8770 }
8771
8772
8773 //-----------------------------------------------------------------------------
8774 // [SECTION] DRAG AND DROP
8775 //-----------------------------------------------------------------------------
8776
ClearDragDrop()8777 void ImGui::ClearDragDrop()
8778 {
8779 ImGuiContext& g = *GImGui;
8780 g.DragDropActive = false;
8781 g.DragDropPayload.Clear();
8782 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8783 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8784 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8785 g.DragDropAcceptFrameCount = -1;
8786
8787 g.DragDropPayloadBufHeap.clear();
8788 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8789 }
8790
8791 // Call when current ID is active.
8792 // 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)8793 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8794 {
8795 ImGuiContext& g = *GImGui;
8796 ImGuiWindow* window = g.CurrentWindow;
8797
8798 bool source_drag_active = false;
8799 ImGuiID source_id = 0;
8800 ImGuiID source_parent_id = 0;
8801 int mouse_button = 0;
8802 if (!(flags & ImGuiDragDropFlags_SourceExtern))
8803 {
8804 source_id = window->DC.LastItemId;
8805 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8806 return false;
8807 if (g.IO.MouseDown[mouse_button] == false)
8808 return false;
8809
8810 if (source_id == 0)
8811 {
8812 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8813 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8814 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8815 {
8816 IM_ASSERT(0);
8817 return false;
8818 }
8819
8820 // Early out
8821 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8822 return false;
8823
8824 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8825 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8826 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8827 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8828 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8829 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id);
8830 if (is_hovered && g.IO.MouseClicked[mouse_button])
8831 {
8832 SetActiveID(source_id, window);
8833 FocusWindow(window);
8834 }
8835 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8836 g.ActiveIdAllowOverlap = is_hovered;
8837 }
8838 else
8839 {
8840 g.ActiveIdAllowOverlap = false;
8841 }
8842 if (g.ActiveId != source_id)
8843 return false;
8844 source_parent_id = window->IDStack.back();
8845 source_drag_active = IsMouseDragging(mouse_button);
8846 }
8847 else
8848 {
8849 window = NULL;
8850 source_id = ImHashStr("#SourceExtern");
8851 source_drag_active = true;
8852 }
8853
8854 if (source_drag_active)
8855 {
8856 if (!g.DragDropActive)
8857 {
8858 IM_ASSERT(source_id != 0);
8859 ClearDragDrop();
8860 ImGuiPayload& payload = g.DragDropPayload;
8861 payload.SourceId = source_id;
8862 payload.SourceParentId = source_parent_id;
8863 g.DragDropActive = true;
8864 g.DragDropSourceFlags = flags;
8865 g.DragDropMouseButton = mouse_button;
8866 }
8867 g.DragDropSourceFrameCount = g.FrameCount;
8868 g.DragDropWithinSourceOrTarget = true;
8869
8870 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8871 {
8872 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8873 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8874 BeginTooltip();
8875 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8876 {
8877 ImGuiWindow* tooltip_window = g.CurrentWindow;
8878 tooltip_window->SkipItems = true;
8879 tooltip_window->HiddenFramesCanSkipItems = 1;
8880 }
8881 }
8882
8883 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8884 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8885
8886 return true;
8887 }
8888 return false;
8889 }
8890
EndDragDropSource()8891 void ImGui::EndDragDropSource()
8892 {
8893 ImGuiContext& g = *GImGui;
8894 IM_ASSERT(g.DragDropActive);
8895 IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8896
8897 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8898 EndTooltip();
8899
8900 // Discard the drag if have not called SetDragDropPayload()
8901 if (g.DragDropPayload.DataFrameCount == -1)
8902 ClearDragDrop();
8903 g.DragDropWithinSourceOrTarget = false;
8904 }
8905
8906 // 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)8907 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8908 {
8909 ImGuiContext& g = *GImGui;
8910 ImGuiPayload& payload = g.DragDropPayload;
8911 if (cond == 0)
8912 cond = ImGuiCond_Always;
8913
8914 IM_ASSERT(type != NULL);
8915 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8916 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8917 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8918 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
8919
8920 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8921 {
8922 // Copy payload
8923 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8924 g.DragDropPayloadBufHeap.resize(0);
8925 if (data_size > sizeof(g.DragDropPayloadBufLocal))
8926 {
8927 // Store in heap
8928 g.DragDropPayloadBufHeap.resize((int)data_size);
8929 payload.Data = g.DragDropPayloadBufHeap.Data;
8930 memcpy(payload.Data, data, data_size);
8931 }
8932 else if (data_size > 0)
8933 {
8934 // Store locally
8935 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8936 payload.Data = g.DragDropPayloadBufLocal;
8937 memcpy(payload.Data, data, data_size);
8938 }
8939 else
8940 {
8941 payload.Data = NULL;
8942 }
8943 payload.DataSize = (int)data_size;
8944 }
8945 payload.DataFrameCount = g.FrameCount;
8946
8947 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8948 }
8949
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8950 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8951 {
8952 ImGuiContext& g = *GImGui;
8953 if (!g.DragDropActive)
8954 return false;
8955
8956 ImGuiWindow* window = g.CurrentWindow;
8957 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8958 return false;
8959 IM_ASSERT(id != 0);
8960 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8961 return false;
8962 if (window->SkipItems)
8963 return false;
8964
8965 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8966 g.DragDropTargetRect = bb;
8967 g.DragDropTargetId = id;
8968 g.DragDropWithinSourceOrTarget = true;
8969 return true;
8970 }
8971
8972 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8973 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8974 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8975 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8976 bool ImGui::BeginDragDropTarget()
8977 {
8978 ImGuiContext& g = *GImGui;
8979 if (!g.DragDropActive)
8980 return false;
8981
8982 ImGuiWindow* window = g.CurrentWindow;
8983 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8984 return false;
8985 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8986 return false;
8987
8988 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8989 ImGuiID id = window->DC.LastItemId;
8990 if (id == 0)
8991 id = window->GetIDFromRectangle(display_rect);
8992 if (g.DragDropPayload.SourceId == id)
8993 return false;
8994
8995 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8996 g.DragDropTargetRect = display_rect;
8997 g.DragDropTargetId = id;
8998 g.DragDropWithinSourceOrTarget = true;
8999 return true;
9000 }
9001
IsDragDropPayloadBeingAccepted()9002 bool ImGui::IsDragDropPayloadBeingAccepted()
9003 {
9004 ImGuiContext& g = *GImGui;
9005 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
9006 }
9007
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)9008 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
9009 {
9010 ImGuiContext& g = *GImGui;
9011 ImGuiWindow* window = g.CurrentWindow;
9012 ImGuiPayload& payload = g.DragDropPayload;
9013 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
9014 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
9015 if (type != NULL && !payload.IsDataType(type))
9016 return NULL;
9017
9018 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
9019 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
9020 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
9021 ImRect r = g.DragDropTargetRect;
9022 float r_surface = r.GetWidth() * r.GetHeight();
9023 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
9024 {
9025 g.DragDropAcceptFlags = flags;
9026 g.DragDropAcceptIdCurr = g.DragDropTargetId;
9027 g.DragDropAcceptIdCurrRectSurface = r_surface;
9028 }
9029
9030 // Render default drop visuals
9031 payload.Preview = was_accepted_previously;
9032 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
9033 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
9034 {
9035 // FIXME-DRAG: Settle on a proper default visuals for drop target.
9036 r.Expand(3.5f);
9037 bool push_clip_rect = !window->ClipRect.Contains(r);
9038 if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
9039 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
9040 if (push_clip_rect) window->DrawList->PopClipRect();
9041 }
9042
9043 g.DragDropAcceptFrameCount = g.FrameCount;
9044 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()
9045 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
9046 return NULL;
9047
9048 return &payload;
9049 }
9050
GetDragDropPayload()9051 const ImGuiPayload* ImGui::GetDragDropPayload()
9052 {
9053 ImGuiContext& g = *GImGui;
9054 return g.DragDropActive ? &g.DragDropPayload : NULL;
9055 }
9056
9057 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()9058 void ImGui::EndDragDropTarget()
9059 {
9060 ImGuiContext& g = *GImGui;
9061 IM_ASSERT(g.DragDropActive);
9062 IM_ASSERT(g.DragDropWithinSourceOrTarget);
9063 g.DragDropWithinSourceOrTarget = false;
9064 }
9065
9066
9067 //-----------------------------------------------------------------------------
9068 // [SECTION] LOGGING/CAPTURING
9069 //-----------------------------------------------------------------------------
9070 // All text output from the interface can be captured into tty/file/clipboard.
9071 // By default, tree nodes are automatically opened during logging.
9072 //-----------------------------------------------------------------------------
9073
9074 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)9075 void ImGui::LogText(const char* fmt, ...)
9076 {
9077 ImGuiContext& g = *GImGui;
9078 if (!g.LogEnabled)
9079 return;
9080
9081 va_list args;
9082 va_start(args, fmt);
9083 if (g.LogFile)
9084 {
9085 g.LogBuffer.Buf.resize(0);
9086 g.LogBuffer.appendfv(fmt, args);
9087 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
9088 }
9089 else
9090 {
9091 g.LogBuffer.appendfv(fmt, args);
9092 }
9093 va_end(args);
9094 }
9095
9096 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
9097 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)9098 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
9099 {
9100 ImGuiContext& g = *GImGui;
9101 ImGuiWindow* window = g.CurrentWindow;
9102
9103 if (!text_end)
9104 text_end = FindRenderedTextEnd(text, text_end);
9105
9106 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1);
9107 if (ref_pos)
9108 g.LogLinePosY = ref_pos->y;
9109 if (log_new_line)
9110 g.LogLineFirstItem = true;
9111
9112 const char* text_remaining = text;
9113 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
9114 g.LogDepthRef = window->DC.TreeDepth;
9115 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
9116 for (;;)
9117 {
9118 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
9119 // We don't add a trailing \n to allow a subsequent item on the same line to be captured.
9120 const char* line_start = text_remaining;
9121 const char* line_end = ImStreolRange(line_start, text_end);
9122 const bool is_first_line = (line_start == text);
9123 const bool is_last_line = (line_end == text_end);
9124 if (!is_last_line || (line_start != line_end))
9125 {
9126 const int char_count = (int)(line_end - line_start);
9127 if (log_new_line || !is_first_line)
9128 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start);
9129 else if (g.LogLineFirstItem)
9130 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start);
9131 else
9132 LogText(" %.*s", char_count, line_start);
9133 g.LogLineFirstItem = false;
9134 }
9135 else if (log_new_line)
9136 {
9137 // An empty "" string at a different Y position should output a carriage return.
9138 LogText(IM_NEWLINE);
9139 break;
9140 }
9141
9142 if (is_last_line)
9143 break;
9144 text_remaining = line_end + 1;
9145 }
9146 }
9147
9148 // Start logging/capturing text output
LogBegin(ImGuiLogType type,int auto_open_depth)9149 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)
9150 {
9151 ImGuiContext& g = *GImGui;
9152 ImGuiWindow* window = g.CurrentWindow;
9153 IM_ASSERT(g.LogEnabled == false);
9154 IM_ASSERT(g.LogFile == NULL);
9155 IM_ASSERT(g.LogBuffer.empty());
9156 g.LogEnabled = true;
9157 g.LogType = type;
9158 g.LogDepthRef = window->DC.TreeDepth;
9159 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
9160 g.LogLinePosY = FLT_MAX;
9161 g.LogLineFirstItem = true;
9162 }
9163
LogToTTY(int auto_open_depth)9164 void ImGui::LogToTTY(int auto_open_depth)
9165 {
9166 ImGuiContext& g = *GImGui;
9167 if (g.LogEnabled)
9168 return;
9169 IM_UNUSED(auto_open_depth);
9170 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9171 LogBegin(ImGuiLogType_TTY, auto_open_depth);
9172 g.LogFile = stdout;
9173 #endif
9174 }
9175
9176 // Start logging/capturing text output to given file
LogToFile(int auto_open_depth,const char * filename)9177 void ImGui::LogToFile(int auto_open_depth, const char* filename)
9178 {
9179 ImGuiContext& g = *GImGui;
9180 if (g.LogEnabled)
9181 return;
9182
9183 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
9184 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
9185 // By opening the file in binary mode "ab" we have consistent output everywhere.
9186 if (!filename)
9187 filename = g.IO.LogFilename;
9188 if (!filename || !filename[0])
9189 return;
9190 ImFileHandle f = ImFileOpen(filename, "ab");
9191 if (!f)
9192 {
9193 IM_ASSERT(0);
9194 return;
9195 }
9196
9197 LogBegin(ImGuiLogType_File, auto_open_depth);
9198 g.LogFile = f;
9199 }
9200
9201 // Start logging/capturing text output to clipboard
LogToClipboard(int auto_open_depth)9202 void ImGui::LogToClipboard(int auto_open_depth)
9203 {
9204 ImGuiContext& g = *GImGui;
9205 if (g.LogEnabled)
9206 return;
9207 LogBegin(ImGuiLogType_Clipboard, auto_open_depth);
9208 }
9209
LogToBuffer(int auto_open_depth)9210 void ImGui::LogToBuffer(int auto_open_depth)
9211 {
9212 ImGuiContext& g = *GImGui;
9213 if (g.LogEnabled)
9214 return;
9215 LogBegin(ImGuiLogType_Buffer, auto_open_depth);
9216 }
9217
LogFinish()9218 void ImGui::LogFinish()
9219 {
9220 ImGuiContext& g = *GImGui;
9221 if (!g.LogEnabled)
9222 return;
9223
9224 LogText(IM_NEWLINE);
9225 switch (g.LogType)
9226 {
9227 case ImGuiLogType_TTY:
9228 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9229 fflush(g.LogFile);
9230 #endif
9231 break;
9232 case ImGuiLogType_File:
9233 ImFileClose(g.LogFile);
9234 break;
9235 case ImGuiLogType_Buffer:
9236 break;
9237 case ImGuiLogType_Clipboard:
9238 if (!g.LogBuffer.empty())
9239 SetClipboardText(g.LogBuffer.begin());
9240 break;
9241 case ImGuiLogType_None:
9242 IM_ASSERT(0);
9243 break;
9244 }
9245
9246 g.LogEnabled = false;
9247 g.LogType = ImGuiLogType_None;
9248 g.LogFile = NULL;
9249 g.LogBuffer.clear();
9250 }
9251
9252 // Helper to display logging buttons
9253 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
LogButtons()9254 void ImGui::LogButtons()
9255 {
9256 ImGuiContext& g = *GImGui;
9257
9258 PushID("LogButtons");
9259 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS
9260 const bool log_to_tty = Button("Log To TTY"); SameLine();
9261 #else
9262 const bool log_to_tty = false;
9263 #endif
9264 const bool log_to_file = Button("Log To File"); SameLine();
9265 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
9266 PushAllowKeyboardFocus(false);
9267 SetNextItemWidth(80.0f);
9268 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
9269 PopAllowKeyboardFocus();
9270 PopID();
9271
9272 // Start logging at the end of the function so that the buttons don't appear in the log
9273 if (log_to_tty)
9274 LogToTTY();
9275 if (log_to_file)
9276 LogToFile();
9277 if (log_to_clipboard)
9278 LogToClipboard();
9279 }
9280
9281 //-----------------------------------------------------------------------------
9282 // [SECTION] SETTINGS
9283 //-----------------------------------------------------------------------------
9284
MarkIniSettingsDirty()9285 void ImGui::MarkIniSettingsDirty()
9286 {
9287 ImGuiContext& g = *GImGui;
9288 if (g.SettingsDirtyTimer <= 0.0f)
9289 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9290 }
9291
MarkIniSettingsDirty(ImGuiWindow * window)9292 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
9293 {
9294 ImGuiContext& g = *GImGui;
9295 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
9296 if (g.SettingsDirtyTimer <= 0.0f)
9297 g.SettingsDirtyTimer = g.IO.IniSavingRate;
9298 }
9299
CreateNewWindowSettings(const char * name)9300 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
9301 {
9302 ImGuiContext& g = *GImGui;
9303
9304 #if !IMGUI_DEBUG_INI_SETTINGS
9305 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
9306 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier.
9307 if (const char* p = strstr(name, "###"))
9308 name = p;
9309 #endif
9310 const size_t name_len = strlen(name);
9311
9312 // Allocate chunk
9313 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
9314 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
9315 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
9316 settings->ID = ImHashStr(name, name_len);
9317 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator
9318
9319 return settings;
9320 }
9321
FindWindowSettings(ImGuiID id)9322 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
9323 {
9324 ImGuiContext& g = *GImGui;
9325 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9326 if (settings->ID == id)
9327 return settings;
9328 return NULL;
9329 }
9330
FindOrCreateWindowSettings(const char * name)9331 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name)
9332 {
9333 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name)))
9334 return settings;
9335 return CreateNewWindowSettings(name);
9336 }
9337
LoadIniSettingsFromDisk(const char * ini_filename)9338 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
9339 {
9340 size_t file_data_size = 0;
9341 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
9342 if (!file_data)
9343 return;
9344 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
9345 IM_FREE(file_data);
9346 }
9347
FindSettingsHandler(const char * type_name)9348 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
9349 {
9350 ImGuiContext& g = *GImGui;
9351 const ImGuiID type_hash = ImHashStr(type_name);
9352 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9353 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
9354 return &g.SettingsHandlers[handler_n];
9355 return NULL;
9356 }
9357
9358 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)9359 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
9360 {
9361 ImGuiContext& g = *GImGui;
9362 IM_ASSERT(g.Initialized);
9363 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
9364
9365 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
9366 // 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..
9367 if (ini_size == 0)
9368 ini_size = strlen(ini_data);
9369 char* buf = (char*)IM_ALLOC(ini_size + 1);
9370 char* buf_end = buf + ini_size;
9371 memcpy(buf, ini_data, ini_size);
9372 buf[ini_size] = 0;
9373
9374 void* entry_data = NULL;
9375 ImGuiSettingsHandler* entry_handler = NULL;
9376
9377 char* line_end = NULL;
9378 for (char* line = buf; line < buf_end; line = line_end + 1)
9379 {
9380 // Skip new lines markers, then find end of the line
9381 while (*line == '\n' || *line == '\r')
9382 line++;
9383 line_end = line;
9384 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
9385 line_end++;
9386 line_end[0] = 0;
9387 if (line[0] == ';')
9388 continue;
9389 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
9390 {
9391 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
9392 line_end[-1] = 0;
9393 const char* name_end = line_end - 1;
9394 const char* type_start = line + 1;
9395 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
9396 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
9397 if (!type_end || !name_start)
9398 continue;
9399 *type_end = 0; // Overwrite first ']'
9400 name_start++; // Skip second '['
9401 entry_handler = FindSettingsHandler(type_start);
9402 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
9403 }
9404 else if (entry_handler != NULL && entry_data != NULL)
9405 {
9406 // Let type handler parse the line
9407 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
9408 }
9409 }
9410 IM_FREE(buf);
9411 g.SettingsLoaded = true;
9412 }
9413
SaveIniSettingsToDisk(const char * ini_filename)9414 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
9415 {
9416 ImGuiContext& g = *GImGui;
9417 g.SettingsDirtyTimer = 0.0f;
9418 if (!ini_filename)
9419 return;
9420
9421 size_t ini_data_size = 0;
9422 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
9423 ImFileHandle f = ImFileOpen(ini_filename, "wt");
9424 if (!f)
9425 return;
9426 ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
9427 ImFileClose(f);
9428 }
9429
9430 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)9431 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
9432 {
9433 ImGuiContext& g = *GImGui;
9434 g.SettingsDirtyTimer = 0.0f;
9435 g.SettingsIniData.Buf.resize(0);
9436 g.SettingsIniData.Buf.push_back(0);
9437 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
9438 {
9439 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
9440 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
9441 }
9442 if (out_size)
9443 *out_size = (size_t)g.SettingsIniData.size();
9444 return g.SettingsIniData.c_str();
9445 }
9446
WindowSettingsHandler_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)9447 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
9448 {
9449 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name));
9450 if (!settings)
9451 settings = ImGui::CreateNewWindowSettings(name);
9452 return (void*)settings;
9453 }
9454
WindowSettingsHandler_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)9455 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
9456 {
9457 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
9458 int x, y;
9459 int i;
9460 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) settings->Pos = ImVec2ih((short)x, (short)y);
9461 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) settings->Size = ImVec2ih((short)x, (short)y);
9462 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
9463 }
9464
WindowSettingsHandler_WriteAll(ImGuiContext * ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)9465 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
9466 {
9467 // Gather data from windows that were active during this session
9468 // (if a window wasn't opened in this session we preserve its settings)
9469 ImGuiContext& g = *ctx;
9470 for (int i = 0; i != g.Windows.Size; i++)
9471 {
9472 ImGuiWindow* window = g.Windows[i];
9473 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
9474 continue;
9475
9476 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID);
9477 if (!settings)
9478 {
9479 settings = ImGui::CreateNewWindowSettings(window->Name);
9480 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
9481 }
9482 IM_ASSERT(settings->ID == window->ID);
9483 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y);
9484 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y);
9485 settings->Collapsed = window->Collapsed;
9486 }
9487
9488 // Write to text buffer
9489 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
9490 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
9491 {
9492 const char* settings_name = settings->GetName();
9493 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
9494 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
9495 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
9496 buf->appendf("Collapsed=%d\n", settings->Collapsed);
9497 buf->append("\n");
9498 }
9499 }
9500
9501
9502 //-----------------------------------------------------------------------------
9503 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
9504 //-----------------------------------------------------------------------------
9505
9506 // (this section is filled in the 'docking' branch)
9507
9508
9509 //-----------------------------------------------------------------------------
9510 // [SECTION] DOCKING
9511 //-----------------------------------------------------------------------------
9512
9513 // (this section is filled in the 'docking' branch)
9514
9515
9516 //-----------------------------------------------------------------------------
9517 // [SECTION] PLATFORM DEPENDENT HELPERS
9518 //-----------------------------------------------------------------------------
9519
9520 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9521 #ifndef WIN32_LEAN_AND_MEAN
9522 #define WIN32_LEAN_AND_MEAN
9523 #endif
9524 #ifndef __MINGW32__
9525 #include <Windows.h>
9526 #else
9527 #include <windows.h>
9528 #endif
9529 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions
9530 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
9531 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
9532 #endif
9533 #elif defined(__APPLE__)
9534 #include <TargetConditionals.h>
9535 #endif
9536
9537 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9538
9539 #ifdef _MSC_VER
9540 #pragma comment(lib, "user32")
9541 #endif
9542
9543 // Win32 clipboard implementation
GetClipboardTextFn_DefaultImpl(void *)9544 static const char* GetClipboardTextFn_DefaultImpl(void*)
9545 {
9546 static ImVector<char> buf_local;
9547 buf_local.clear();
9548 if (!::OpenClipboard(NULL))
9549 return NULL;
9550 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9551 if (wbuf_handle == NULL)
9552 {
9553 ::CloseClipboard();
9554 return NULL;
9555 }
9556 if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9557 {
9558 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9559 buf_local.resize(buf_len);
9560 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9561 }
9562 ::GlobalUnlock(wbuf_handle);
9563 ::CloseClipboard();
9564 return buf_local.Data;
9565 }
9566
SetClipboardTextFn_DefaultImpl(void *,const char * text)9567 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9568 {
9569 if (!::OpenClipboard(NULL))
9570 return;
9571 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9572 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9573 if (wbuf_handle == NULL)
9574 {
9575 ::CloseClipboard();
9576 return;
9577 }
9578 ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9579 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9580 ::GlobalUnlock(wbuf_handle);
9581 ::EmptyClipboard();
9582 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9583 ::GlobalFree(wbuf_handle);
9584 ::CloseClipboard();
9585 }
9586
9587 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
9588
9589 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file
9590 static PasteboardRef main_clipboard = 0;
9591
9592 // OSX clipboard implementation
9593 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
SetClipboardTextFn_DefaultImpl(void *,const char * text)9594 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9595 {
9596 if (!main_clipboard)
9597 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9598 PasteboardClear(main_clipboard);
9599 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));
9600 if (cf_data)
9601 {
9602 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
9603 CFRelease(cf_data);
9604 }
9605 }
9606
GetClipboardTextFn_DefaultImpl(void *)9607 static const char* GetClipboardTextFn_DefaultImpl(void*)
9608 {
9609 if (!main_clipboard)
9610 PasteboardCreate(kPasteboardClipboard, &main_clipboard);
9611 PasteboardSynchronize(main_clipboard);
9612
9613 ItemCount item_count = 0;
9614 PasteboardGetItemCount(main_clipboard, &item_count);
9615 for (ItemCount i = 0; i < item_count; i++)
9616 {
9617 PasteboardItemID item_id = 0;
9618 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
9619 CFArrayRef flavor_type_array = 0;
9620 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
9621 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
9622 {
9623 CFDataRef cf_data;
9624 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
9625 {
9626 static ImVector<char> clipboard_text;
9627 int length = (int)CFDataGetLength(cf_data);
9628 clipboard_text.resize(length + 1);
9629 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)clipboard_text.Data);
9630 clipboard_text[length] = 0;
9631 CFRelease(cf_data);
9632 return clipboard_text.Data;
9633 }
9634 }
9635 }
9636 return NULL;
9637 }
9638
9639 #else
9640
9641 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
GetClipboardTextFn_DefaultImpl(void *)9642 static const char* GetClipboardTextFn_DefaultImpl(void*)
9643 {
9644 ImGuiContext& g = *GImGui;
9645 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9646 }
9647
SetClipboardTextFn_DefaultImpl(void *,const char * text)9648 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9649 {
9650 ImGuiContext& g = *GImGui;
9651 g.PrivateClipboard.clear();
9652 const char* text_end = text + strlen(text);
9653 g.PrivateClipboard.resize((int)(text_end - text) + 1);
9654 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9655 g.PrivateClipboard[(int)(text_end - text)] = 0;
9656 }
9657
9658 #endif
9659
9660 // Win32 API IME support (for Asian languages, etc.)
9661 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9662
9663 #include <imm.h>
9664 #ifdef _MSC_VER
9665 #pragma comment(lib, "imm32")
9666 #endif
9667
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9668 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9669 {
9670 // Notify OS Input Method Editor of text input position
9671 ImGuiIO& io = ImGui::GetIO();
9672 if (HWND hwnd = (HWND)io.ImeWindowHandle)
9673 if (HIMC himc = ::ImmGetContext(hwnd))
9674 {
9675 COMPOSITIONFORM cf;
9676 cf.ptCurrentPos.x = x;
9677 cf.ptCurrentPos.y = y;
9678 cf.dwStyle = CFS_FORCE_POSITION;
9679 ::ImmSetCompositionWindow(himc, &cf);
9680 ::ImmReleaseContext(hwnd, himc);
9681 }
9682 }
9683
9684 #else
9685
ImeSetInputScreenPosFn_DefaultImpl(int,int)9686 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9687
9688 #endif
9689
9690 //-----------------------------------------------------------------------------
9691 // [SECTION] METRICS/DEBUG WINDOW
9692 //-----------------------------------------------------------------------------
9693
9694 #ifndef IMGUI_DISABLE_METRICS_WINDOW
9695 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
MetricsHelpMarker(const char * desc)9696 static void MetricsHelpMarker(const char* desc)
9697 {
9698 ImGui::TextDisabled("(?)");
9699 if (ImGui::IsItemHovered())
9700 {
9701 ImGui::BeginTooltip();
9702 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
9703 ImGui::TextUnformatted(desc);
9704 ImGui::PopTextWrapPos();
9705 ImGui::EndTooltip();
9706 }
9707 }
9708
ShowMetricsWindow(bool * p_open)9709 void ImGui::ShowMetricsWindow(bool* p_open)
9710 {
9711 if (!ImGui::Begin("Dear ImGui Metrics", p_open))
9712 {
9713 ImGui::End();
9714 return;
9715 }
9716
9717 // State
9718 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
9719 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" };
9720 static bool show_windows_rects = false;
9721 static int show_windows_rect_type = WRT_WorkRect;
9722 static bool show_windows_begin_order = false;
9723 static bool show_drawcmd_details = true;
9724
9725 // Basic info
9726 ImGuiContext& g = *GImGui;
9727 ImGuiIO& io = ImGui::GetIO();
9728 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9729 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9730 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9731 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9732 ImGui::Text("%d active allocations", io.MetricsActiveAllocations);
9733 ImGui::Separator();
9734
9735 // Helper functions to display common structures:
9736 // - NodeDrawList
9737 // - NodeColumns
9738 // - NodeWindow
9739 // - NodeWindows
9740 // - NodeTabBar
9741 struct Funcs
9742 {
9743 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
9744 {
9745 if (rect_type == WRT_OuterRect) { return window->Rect(); }
9746 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; }
9747 else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
9748 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
9749 else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
9750 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
9751 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
9752 IM_ASSERT(0);
9753 return ImRect();
9754 }
9755
9756 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9757 {
9758 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);
9759 if (draw_list == ImGui::GetWindowDrawList())
9760 {
9761 ImGui::SameLine();
9762 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)
9763 if (node_open) ImGui::TreePop();
9764 return;
9765 }
9766
9767 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
9768 if (window && IsItemHovered())
9769 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9770 if (!node_open)
9771 return;
9772
9773 if (window && !window->WasActive)
9774 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
9775
9776 unsigned int elem_offset = 0;
9777 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9778 {
9779 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9780 continue;
9781 if (pcmd->UserCallback)
9782 {
9783 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9784 continue;
9785 }
9786
9787 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9788 char buf[300];
9789 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd: %4d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
9790 pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId,
9791 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9792 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
9793 if (show_drawcmd_details && fg_draw_list && ImGui::IsItemHovered())
9794 {
9795 ImRect clip_rect = pcmd->ClipRect;
9796 ImRect vtxs_rect;
9797 for (unsigned int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9798 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9799 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255,0,255,255));
9800 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(255,255,0,255));
9801 }
9802 if (!pcmd_node_open)
9803 continue;
9804
9805 // Calculate approximate coverage area (touched pixel count)
9806 // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
9807 float total_area = 0.0f;
9808 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9809 {
9810 ImVec2 triangle[3];
9811 for (int n = 0; n < 3; n++)
9812 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9813 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
9814 }
9815
9816 // Display vertex information summary. Hover to get all triangles drawn in wire-frame
9817 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
9818 ImGui::Selectable(buf);
9819 if (fg_draw_list && ImGui::IsItemHovered() && show_drawcmd_details)
9820 {
9821 // Draw wire-frame version of everything
9822 ImDrawListFlags backup_flags = fg_draw_list->Flags;
9823 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
9824 ImRect clip_rect = pcmd->ClipRect;
9825 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255));
9826 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3)
9827 {
9828 ImVec2 triangle[3];
9829 for (int n = 0; n < 3; n++)
9830 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos;
9831 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f);
9832 }
9833 fg_draw_list->Flags = backup_flags;
9834 }
9835
9836 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9837 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9838 while (clipper.Step())
9839 for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9840 {
9841 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9842 ImVec2 triangle[3];
9843 for (int n = 0; n < 3; n++, idx_i++)
9844 {
9845 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
9846 triangle[n] = v.pos;
9847 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
9848 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9849 }
9850
9851 ImGui::Selectable(buf, false);
9852 if (fg_draw_list && ImGui::IsItemHovered())
9853 {
9854 ImDrawListFlags backup_flags = fg_draw_list->Flags;
9855 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
9856 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f);
9857 fg_draw_list->Flags = backup_flags;
9858 }
9859 }
9860 ImGui::TreePop();
9861 }
9862 ImGui::TreePop();
9863 }
9864
9865 static void NodeColumns(const ImGuiColumns* columns)
9866 {
9867 if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9868 return;
9869 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
9870 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9871 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm));
9872 ImGui::TreePop();
9873 }
9874
9875 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9876 {
9877 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9878 return;
9879 for (int i = 0; i < windows.Size; i++)
9880 Funcs::NodeWindow(windows[i], "Window");
9881 ImGui::TreePop();
9882 }
9883
9884 static void NodeWindow(ImGuiWindow* window, const char* label)
9885 {
9886 if (window == NULL)
9887 {
9888 ImGui::BulletText("%s: NULL", label);
9889 return;
9890 }
9891 bool open = ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window);
9892 if (ImGui::IsItemHovered() && window->WasActive)
9893 ImGui::GetForegroundDrawList()->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9894 if (!open)
9895 return;
9896 ImGuiWindowFlags flags = window->Flags;
9897 NodeDrawList(window, window->DrawList, "DrawList");
9898 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);
9899 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9900 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9901 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9902 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9903 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" : "");
9904 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9905 ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
9906 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9907 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9908 if (!window->NavRectRel[0].IsInverted())
9909 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);
9910 else
9911 ImGui::BulletText("NavRectRel[0]: <None>");
9912 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9913 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9914 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9915 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9916 {
9917 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9918 NodeColumns(&window->ColumnsStorage[n]);
9919 ImGui::TreePop();
9920 }
9921 NodeStorage(&window->StateStorage, "Storage");
9922 ImGui::TreePop();
9923 }
9924
9925 static void NodeTabBar(ImGuiTabBar* tab_bar)
9926 {
9927 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
9928 char buf[256];
9929 char* p = buf;
9930 const char* buf_end = buf + IM_ARRAYSIZE(buf);
9931 ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9932 if (ImGui::TreeNode(tab_bar, "%s", buf))
9933 {
9934 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9935 {
9936 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9937 ImGui::PushID(tab);
9938 if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9939 if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9940 ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "");
9941 ImGui::PopID();
9942 }
9943 ImGui::TreePop();
9944 }
9945 }
9946
9947 static void NodeStorage(ImGuiStorage* storage, const char* label)
9948 {
9949 if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
9950 return;
9951 for (int n = 0; n < storage->Data.Size; n++)
9952 {
9953 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n];
9954 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.
9955 }
9956 ImGui::TreePop();
9957 }
9958 };
9959
9960 Funcs::NodeWindows(g.Windows, "Windows");
9961 if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9962 {
9963 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9964 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9965 ImGui::TreePop();
9966 }
9967
9968 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9969 {
9970 for (int i = 0; i < g.OpenPopupStack.Size; i++)
9971 {
9972 ImGuiWindow* window = g.OpenPopupStack[i].Window;
9973 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" : "");
9974 }
9975 ImGui::TreePop();
9976 }
9977
9978 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize()))
9979 {
9980 for (int n = 0; n < g.TabBars.GetSize(); n++)
9981 Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9982 ImGui::TreePop();
9983 }
9984
9985 #if 0
9986 if (ImGui::TreeNode("Docking"))
9987 {
9988 ImGui::TreePop();
9989 }
9990 #endif
9991
9992 #if 0
9993 if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize()))
9994 {
9995 ImGui::TreePop();
9996 }
9997 #endif
9998
9999 if (ImGui::TreeNode("Internal state"))
10000 {
10001 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10002 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
10003 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
10004 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
10005 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]);
10006 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
10007 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
10008 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
10009 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
10010 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
10011 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
10012 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
10013 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
10014 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
10015 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
10016 ImGui::TreePop();
10017 }
10018
10019 if (ImGui::TreeNode("Tools"))
10020 {
10021 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
10022 if (ImGui::Button("Item Picker.."))
10023 ImGui::DebugStartItemPicker();
10024 ImGui::SameLine();
10025 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.");
10026
10027 ImGui::Checkbox("Show windows begin order", &show_windows_begin_order);
10028 ImGui::Checkbox("Show windows rectangles", &show_windows_rects);
10029 ImGui::SameLine();
10030 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12);
10031 show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count);
10032 if (show_windows_rects && g.NavWindow)
10033 {
10034 ImGui::BulletText("'%s':", g.NavWindow->Name);
10035 ImGui::Indent();
10036 for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
10037 {
10038 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
10039 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]);
10040 }
10041 ImGui::Unindent();
10042 }
10043 ImGui::Checkbox("Show details when hovering ImDrawCmd node", &show_drawcmd_details);
10044 ImGui::TreePop();
10045 }
10046
10047 // Tool: Display windows Rectangles and Begin Order
10048 if (show_windows_rects || show_windows_begin_order)
10049 {
10050 for (int n = 0; n < g.Windows.Size; n++)
10051 {
10052 ImGuiWindow* window = g.Windows[n];
10053 if (!window->WasActive)
10054 continue;
10055 ImDrawList* draw_list = GetForegroundDrawList(window);
10056 if (show_windows_rects)
10057 {
10058 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type);
10059 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
10060 }
10061 if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow))
10062 {
10063 char buf[32];
10064 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
10065 float font_size = ImGui::GetFontSize();
10066 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
10067 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
10068 }
10069 }
10070 }
10071 ImGui::End();
10072 }
10073
10074 #else
10075
ShowMetricsWindow(bool *)10076 void ImGui::ShowMetricsWindow(bool*) { }
10077
10078 #endif
10079
10080 //-----------------------------------------------------------------------------
10081
10082 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
10083 // 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.
10084 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
10085 #include "imgui_user.inl"
10086 #endif
10087
10088 //-----------------------------------------------------------------------------
10089