1 // dear imgui, v1.67
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://discourse.dearimgui.org/c/getting-started
9 // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269
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 (read me!)
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 - This is how a simple application may look like (2 variations).
36 - This is 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), TIPS
40 - How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
41 - How can I display an image? What is ImTextureID, how does it works?
42 - How can I have multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack.
43 - How can I use my own math types instead of ImVec2/ImVec4?
44 - How can I load a different font than the default?
45 - How can I easily use icons in my application?
46 - How can I load multiple fonts?
47 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
48 - How can I interact with standard C++ types (such as std::string and std::vector)?
49 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
50 - How can I use Dear ImGui on a platform that doesn't have a mouse or a keyboard? (input share, remoting, gamepad)
51 - I integrated Dear ImGui in my engine and the text or lines are blurry..
52 - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
53 - How can I help?
54
55 CODE
56 (search for "[SECTION]" in the code to find them)
57
58 // [SECTION] FORWARD DECLARATIONS
59 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
60 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
61 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
62 // [SECTION] MISC HELPER/UTILITIES (ImText* functions)
63 // [SECTION] MISC HELPER/UTILITIES (Color functions)
64 // [SECTION] ImGuiStorage
65 // [SECTION] ImGuiTextFilter
66 // [SECTION] ImGuiTextBuffer
67 // [SECTION] ImGuiListClipper
68 // [SECTION] RENDER HELPERS
69 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
70 // [SECTION] TOOLTIPS
71 // [SECTION] POPUPS
72 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
73 // [SECTION] COLUMNS
74 // [SECTION] DRAG AND DROP
75 // [SECTION] LOGGING/CAPTURING
76 // [SECTION] SETTINGS
77 // [SECTION] PLATFORM DEPENDENT HELPERS
78 // [SECTION] METRICS/DEBUG WINDOW
79
80 */
81
82 //-----------------------------------------------------------------------------
83 // DOCUMENTATION
84 //-----------------------------------------------------------------------------
85
86 /*
87
88 MISSION STATEMENT
89 =================
90
91 - Easy to use to create code-driven and data-driven tools.
92 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
93 - Easy to hack and improve.
94 - Minimize screen real-estate usage.
95 - Minimize setup and maintenance.
96 - Minimize state storage on user side.
97 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
98 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,.
99 opening a tree node for the first time, etc. but a typical frame should not allocate anything).
100
101 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
102 - Doesn't look fancy, doesn't animate.
103 - Limited layout features, intricate layouts are typically crafted in code.
104
105
106 END-USER GUIDE
107 ==============
108
109 - Double-click on title bar to collapse window.
110 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
111 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
112 - Click and drag on any empty space to move window.
113 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
114 - CTRL+Click on a slider or drag box to input value as text.
115 - Use mouse wheel to scroll.
116 - Text editor:
117 - Hold SHIFT or use mouse to select text.
118 - CTRL+Left/Right to word jump.
119 - CTRL+Shift+Left/Right to select words.
120 - CTRL+A our Double-Click to select all.
121 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
122 - CTRL+Z,CTRL+Y to undo/redo.
123 - ESCAPE to revert text to its original value.
124 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
125 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
126 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
127 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
128
129
130 PROGRAMMER GUIDE
131 ================
132
133 READ FIRST:
134
135 - Read the FAQ below this section!
136 - 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
137 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs.
138 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
139 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
140 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
141 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md.
142 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
143 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,
144 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
145 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
146 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings.
147 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
148 If you get an assert, read the messages and comments around the assert.
149 - 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.
150 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
151 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
152 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase.
153 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!).
154
155 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI:
156
157 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
158 - Or maintain your own branch where you have imconfig.h modified.
159 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
160 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
161 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
162 likely be a comment about it. Please report any issue to the GitHub page!
163 - Try to keep your copy of dear imgui reasonably up to date.
164
165 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE:
166
167 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
168 - Add the Dear ImGui source files to your projects or using your preferred build system.
169 It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL).
170 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types.
171 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
172 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
173 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
174 phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
175 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
176 - 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.
177
178 HOW A SIMPLE APPLICATION MAY LOOK LIKE:
179 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder).
180
181 // Application init: create a dear imgui context, setup some options, load fonts
182 ImGui::CreateContext();
183 ImGuiIO& io = ImGui::GetIO();
184 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
185 // TODO: Fill optional fields of the io structure later.
186 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
187
188 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
189 ImGui_ImplWin32_Init(hwnd);
190 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
191
192 // Application main loop
193 while (true)
194 {
195 // Feed inputs to dear imgui, start new frame
196 ImGui_ImplDX11_NewFrame();
197 ImGui_ImplWin32_NewFrame();
198 ImGui::NewFrame();
199
200 // Any application code here
201 ImGui::Text("Hello, world!");
202
203 // Render dear imgui into screen
204 ImGui::Render();
205 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
206 g_pSwapChain->Present(1, 0);
207 }
208
209 // Shutdown
210 ImGui_ImplDX11_Shutdown();
211 ImGui_ImplWin32_Shutdown();
212 ImGui::DestroyContext();
213
214 HOW A SIMPLE APPLICATION MAY LOOK LIKE:
215 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE.
216
217 // Application init: create a dear imgui context, setup some options, load fonts
218 ImGui::CreateContext();
219 ImGuiIO& io = ImGui::GetIO();
220 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
221 // TODO: Fill optional fields of the io structure later.
222 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
223
224 // Build and load the texture atlas into a texture
225 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
226 int width, height;
227 unsigned char* pixels = NULL;
228 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
229
230 // At this point you've got the texture data and you need to upload that your your graphic system:
231 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
232 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
233 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
234 io.Fonts->TexID = (void*)texture;
235
236 // Application main loop
237 while (true)
238 {
239 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
240 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
241 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
242 io.DisplaySize.x = 1920.0f; // set the current display width
243 io.DisplaySize.y = 1280.0f; // set the current display height here
244 io.MousePos = my_mouse_pos; // set the mouse position
245 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
246 io.MouseDown[1] = my_mouse_buttons[1];
247
248 // Call NewFrame(), after this point you can use ImGui::* functions anytime
249 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
250 ImGui::NewFrame();
251
252 // Most of your application code here
253 ImGui::Text("Hello, world!");
254 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
255 MyGameRender(); // may use any ImGui functions as well!
256
257 // Render imgui, swap buffers
258 // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
259 ImGui::EndFrame();
260 ImGui::Render();
261 ImDrawData* draw_data = ImGui::GetDrawData();
262 MyImGuiRenderFunction(draw_data);
263 SwapBuffers();
264 }
265
266 // Shutdown
267 ImGui::DestroyContext();
268
269 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE:
270
271 void void MyImGuiRenderFunction(ImDrawData* draw_data)
272 {
273 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
274 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
275 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
276 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
277 for (int n = 0; n < draw_data->CmdListsCount; n++)
278 {
279 const ImDrawList* cmd_list = draw_data->CmdLists[n];
280 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui
281 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui
282 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
283 {
284 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
285 if (pcmd->UserCallback)
286 {
287 pcmd->UserCallback(cmd_list, pcmd);
288 }
289 else
290 {
291 // The texture for the draw call is specified by pcmd->TextureId.
292 // The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization.
293 MyEngineBindTexture((MyTexture*)pcmd->TextureId);
294
295 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
296 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
297 // (some elements visible outside their bounds) but you can fix that once everything else works!
298 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
299 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
300 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github),
301 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.
302 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
303 ImVec2 pos = draw_data->DisplayPos;
304 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));
305
306 // Render 'pcmd->ElemCount/3' indexed triangles.
307 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices.
308 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
309 }
310 idx_buffer += pcmd->ElemCount;
311 }
312 }
313 }
314
315 - The examples/ folders contains many actual implementation of the pseudo-codes above.
316 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
317 They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs
318 from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information.
319 - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues!
320
321 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
322
323 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
324 - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse!
325 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
326 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
327 - Gamepad:
328 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
329 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
330 Note that io.NavInputs[] is cleared by EndFrame().
331 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
332 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
333 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
334 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.).
335 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
336 - 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
337 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
338 - Keyboard:
339 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
340 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
341 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
342 will be set. For more advanced uses, you may want to read from:
343 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
344 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
345 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
346 Please reach out if you think the game vs navigation input sharing could be improved.
347 - Mouse:
348 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
349 - 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.
350 - 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.
351 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
352 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.
353 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.
354 (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!)
355 (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
356 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
357
358
359 API BREAKING CHANGES
360 ====================
361
362 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
363 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.
364 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.
365 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
366
367 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
368 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete).
369 - 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.
370 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
371 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
372 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
373 - 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.
374 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
375 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
376 - 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.
377 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
378 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
379 - 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).
380 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
381 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
382 - 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.
383 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
384 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
385 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
386 - 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.).
387 old binding will still work as is, however prefer using the separated bindings as they will be updated to be multi-viewport conformant.
388 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call.
389 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
390 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
391 - 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.
392 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
393 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.
394 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.
395 - 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",
396 consistent with other functions. Kept redirection functions (will obsolete).
397 - 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.
398 - 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).
399 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
400 - 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.
401 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
402 - 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.
403 - 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.
404 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
405 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
406 - removed Shutdown() function, as DestroyContext() serve this purpose.
407 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
408 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
409 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
410 - 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.
411 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
412 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
413 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
414 - 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.
415 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
416 - 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
417 - 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.
418 - 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.
419 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
420 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
421 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
422 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
423 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
424 - 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.
425 - 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.
426 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.
427 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
428 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
429 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
430 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
431 - 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.
432 - 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.
433 - 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.
434 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
435 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
436 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
437 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Keep redirection typedef (will obsolete).
438 - 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).
439 - 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)".
440 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
441 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
442 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
443 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
444 - 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.
445 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
446 - 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.
447 - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
448 - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
449 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
450 - 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.
451 - 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.
452 - 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))'
453 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
454 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
455 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
456 - 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().
457 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
458 - 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.
459 - 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.
460 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
461 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
462 If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
463 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.
464 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
465 {
466 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
467 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);
468 }
469 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.
470 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
471 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
472 - 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).
473 - 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.
474 - 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).
475 - 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)
476 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
477 - 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.
478 - 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.
479 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
480 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
481 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
482 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.
483 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!
484 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
485 - 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.
486 - 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
487 - 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.
488 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
489 - 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.
490 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
491 - 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.
492 - the signature of the io.RenderDrawListsFn handler has changed!
493 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
494 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
495 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
496 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
497 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
498 - 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.
499 - 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!
500 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
501 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
502 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
503 - 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.
504 - 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
505 - 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!
506 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
507 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
508 - 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.
509 - 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.
510 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
511 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
512 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
513 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
514 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
515 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
516 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
517 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
518 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
519 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
520 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
521 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
522 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
523 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
524 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
525 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
526 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
527 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
528 font init: { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; }
529 became: { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; }
530 you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
531 it is now recommended that you sample the font texture with bilinear interpolation.
532 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
533 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
534 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
535 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
536 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
537 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
538 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
539 - 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)
540 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
541 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
542 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
543 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
544 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
545 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
546
547
548 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
549 ======================================
550
551 Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
552 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
553 - 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.
554 - 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.
555 - 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).
556 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
557 This is because imgui needs to detect that you clicked in the void to unfocus its own windows.
558 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
559 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.
560 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
561 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().
562 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
563 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
564 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
565
566 Q: How can I display an image? What is ImTextureID, how does it works?
567 A: Short explanation:
568 - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures.
569 - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value.
570 - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
571 Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
572
573 Long explanation:
574 - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
575 At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code
576 to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.).
577 - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API.
578 We carry the information to identify a "texture" in the ImTextureID type.
579 ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice.
580 Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
581 - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
582 an image from the end-user perspective. This is what the _examples_ rendering functions are using:
583
584 OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
585 DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp)
586 DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp)
587 DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp)
588
589 For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
590 Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
591 tying together both the texture and information about its format and how to read it.
592 - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about
593 the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase
594 is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
595 If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID
596 representation suggested by the example bindings is probably the best choice.
597 (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
598
599 User code may do:
600
601 // Cast our texture type to ImTextureID / void*
602 MyTexture* texture = g_CoffeeTableTexture;
603 ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
604
605 The renderer function called after ImGui::Render() will receive that same value that the user code passed:
606
607 // Cast ImTextureID / void* stored in the draw command as our texture type
608 MyTexture* texture = (MyTexture*)pcmd->TextureId;
609 MyEngineBindTexture2D(texture);
610
611 Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui.
612 This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them.
613 If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using.
614
615 Here's a simplified OpenGL example using stb_image.h:
616
617 // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
618 #define STB_IMAGE_IMPLEMENTATION
619 #include <stb_image.h>
620 [...]
621 int my_image_width, my_image_height;
622 unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
623
624 // Turn the RGBA pixel data into an OpenGL texture:
625 GLuint my_opengl_texture;
626 glGenTextures(1, &my_opengl_texture);
627 glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
628 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
629 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
630 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
631 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
632
633 // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it:
634 ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
635
636 C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa.
637 Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
638 Examples:
639
640 GLuint my_tex = XXX;
641 void* my_void_ptr;
642 my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer)
643 my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint
644
645 ID3D11ShaderResourceView* my_dx11_srv = XXX;
646 void* my_void_ptr;
647 my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void*
648 my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView*
649
650 Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
651
652 Q: How can I have multiple widgets with the same label or with an empty label?
653 Q: I have multiple widgets with the same label, and only the first one works. Why is that?
654 A: A primer on labels and the ID Stack...
655
656 Dear ImGui internally need to uniquely identify UI elements.
657 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
658 Interactive widgets (such as calls to Button buttons) need a unique ID.
659 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
660 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
661
662 - Unique ID are often derived from a string label:
663
664 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
665 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
666
667 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
668 two buttons labeled "OK" in different windows or different tree locations is fine.
669 We used "..." above to signify whatever was already pushed to the ID stack previously:
670
671 Begin("MyWindow");
672 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
673 End();
674 Begin("MyOtherWindow");
675 Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK")
676 End();
677
678 - If you have a same ID twice in the same location, you'll have a conflict:
679
680 Button("OK");
681 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
682
683 Fear not! this is easy to solve and there are many ways to solve it!
684
685 - Solving ID conflict in a simple/local context:
686 When passing a label you can optionally specify extra ID information within string itself.
687 Use "##" to pass a complement to the ID that won't be visible to the end-user.
688 This helps solving the simple collision cases when you know e.g. at compilation time which items
689 are going to be created:
690
691 Begin("MyWindow");
692 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
693 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
694 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
695 End();
696
697 - If you want to completely hide the label, but still need an ID:
698
699 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox!
700
701 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
702 you to animate labels. For example you may want to include varying information in a window title bar,
703 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
704
705 Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "ID")
706 Button("World###ID"); // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different
707
708 sprintf(buf, "My game (%f FPS)###MyGame", fps);
709 Begin(buf); // Variable title, ID = hash of "MyGame"
710
711 - Solving ID conflict in a more general manner:
712 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
713 within the same window. This is the most convenient way of distinguishing ID when iterating and
714 creating many UI elements programmatically.
715 You can push a pointer, a string or an integer value into the ID stack.
716 Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack.
717 At each level of the stack we store the seed used for items at this level of the ID stack.
718
719 Begin("Window");
720 for (int i = 0; i < 100; i++)
721 {
722 PushID(i); // Push i to the id tack
723 Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click")
724 PopID();
725 }
726 for (int i = 0; i < 100; i++)
727 {
728 MyObject* obj = Objects[i];
729 PushID(obj);
730 Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click")
731 PopID();
732 }
733 for (int i = 0; i < 100; i++)
734 {
735 MyObject* obj = Objects[i];
736 PushID(obj->Name);
737 Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click")
738 PopID();
739 }
740 End();
741
742 - You can stack multiple prefixes into the ID stack:
743
744 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
745 PushID("node");
746 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
747 PushID(my_ptr);
748 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
749 PopID();
750 PopID();
751
752 - Tree nodes implicitly creates a scope for you by calling PushID().
753
754 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
755 if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag)
756 {
757 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
758 TreePop();
759 }
760
761 - When working with trees, ID are used to preserve the open/close state of each tree node.
762 Depending on your use cases you may want to use strings, indices or pointers as ID.
763 e.g. when following a single pointer that may change over time, using a static string as ID
764 will preserve your node open/closed state when the targeted object change.
765 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
766 node open/closed state differently. See what makes more sense in your situation!
767
768 Q: How can I use my own math types instead of ImVec2/ImVec4?
769 A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
770 This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
771
772 Q: How can I load a different font than the default?
773 A: Use the font atlas to load the TTF/OTF file you want:
774 ImGuiIO& io = ImGui::GetIO();
775 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
776 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
777 Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code.
778 (Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.)
779 (Read the 'misc/fonts/README.txt' file for more details about font loading.)
780
781 New programmers: remember that in C/C++ and most programming languages if you want to use a
782 backslash \ within a string literal, you need to write it double backslash "\\":
783 io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!)
784 io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT
785 io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT
786
787 Q: How can I easily use icons in my application?
788 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
789 main font. Then you can refer to icons within your strings.
790 You may want to see ImFontConfig::GlyphMinAdvanceX to make your icon look monospace to facilitate alignment.
791 (Read the 'misc/fonts/README.txt' file for more details about icons font loading.)
792
793 Q: How can I load multiple fonts?
794 A: Use the font atlas to pack them into a single texture:
795 (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.)
796
797 ImGuiIO& io = ImGui::GetIO();
798 ImFont* font0 = io.Fonts->AddFontDefault();
799 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
800 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
801 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
802 // the first loaded font gets used by default
803 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
804
805 // Options
806 ImFontConfig config;
807 config.OversampleH = 3;
808 config.OversampleV = 1;
809 config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up
810 config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
811 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config);
812
813 // Combine multiple fonts into one (e.g. for icon fonts)
814 static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
815 ImFontConfig config;
816 config.MergeMode = true;
817 io.Fonts->AddFontDefault();
818 io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
819 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
820
821 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
822 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
823
824 // Add default Japanese ranges
825 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
826
827 // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need)
828 ImVector<ImWchar> ranges;
829 ImFontGlyphRangesBuilder builder;
830 builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
831 builder.AddChar(0x7262); // Add a specific character
832 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
833 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
834 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
835
836 All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
837 by using the u8"hello" syntax. Specifying literal in your source code using a local code page
838 (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
839 Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
840
841 Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
842 The applications in examples/ are doing that.
843 Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
844 You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
845 Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for
846 the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
847
848 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
849 A: - Being highly portable (bindings for several languages, frameworks, programming style, obscure or older platforms/compilers),
850 and aiming for compatibility & performance suitable for every modern real-time game engines, dear imgui does not use
851 any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases.
852 - To use ImGui::InputText() with a std::string or any resizable string class, see misc/cpp/imgui_stdlib.h.
853 - To use combo boxes and list boxes with std::vector or any other data structure: the BeginCombo()/EndCombo() API
854 lets you iterate and submit items yourself, so does the ListBoxHeader()/ListBoxFooter() API.
855 Prefer using them over the old and awkward Combo()/ListBox() api.
856 - Generally for most high-level types you should be able to access the underlying data type.
857 You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code).
858 - Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass
859 to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application.
860 Please bear in mind that using std::string on applications with large amount of UI may incur unsatisfactory performances.
861 Modern implementations of std::string often include small-string optimization (which is often a local buffer) but those
862 are not configurable and not the same across implementations.
863 - If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to excessive amount
864 of heap allocations. Consider using literals, statically sized buffers and your own helper functions. A common pattern
865 is that you will need to build lots of strings on the fly, and their maximum length can be easily be scoped ahead.
866 One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str
867 This is a small helper where you can instance strings with configurable local buffers length. Many game engines will
868 provide similar or better string helpers.
869
870 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
871 A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags.
872 (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse)
873 Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
874 - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
875 - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create
876 your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data.
877
878 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display)
879 A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls".
880 (short version: map gamepad inputs into the io.NavInputs[] array + set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad)
881 - You can share your computer mouse seamlessly with your console/tablet/phone using Synergy (https://symless.com/synergy)
882 This is the preferred solution for developer productivity.
883 In particular, the "micro-synergy-client" repository (https://github.com/symless/micro-synergy-client) has simple
884 and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect
885 to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer.
886 Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols.
887 - You may also use a third party solution such as Remote ImGui (https://github.com/JordiRos/remoteimgui) which sends
888 the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine.
889 - For touch inputs, you can increase the hit box of widgets (via the style.TouchPadding setting) to accommodate
890 for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing
891 for screen real-estate and precision.
892
893 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
894 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
895 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
896
897 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
898 A: You are probably mishandling the clipping rectangles in your render function.
899 Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
900
901 Q: How can I help?
902 A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt
903 and see how you want to help and can help!
904 - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui.
905 - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README.
906 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
907 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1902). Visuals are ideal as they inspire other programmers.
908 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
909 - 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).
910
911 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
912 this is also useful to set yourself in the context of another window (to get/set other settings)
913 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
914 - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle
915 of a deep nested inner loop in your code.
916 - tip: you can call Render() multiple times (e.g for VR renders).
917 - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
918
919 */
920
921 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
922 #define _CRT_SECURE_NO_WARNINGS
923 #endif
924
925 #include "imgui.h"
926 #ifndef IMGUI_DEFINE_MATH_OPERATORS
927 #define IMGUI_DEFINE_MATH_OPERATORS
928 #endif
929 #include "imgui_internal.h"
930
931 #include <ctype.h> // toupper, isprint
932 #include <stdio.h> // vsnprintf, sscanf, printf
933 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
934 #include <stddef.h> // intptr_t
935 #else
936 #include <stdint.h> // intptr_t
937 #endif
938
939 // Debug options
940 #define IMGUI_DEBUG_NAV_SCORING 0
941 #define IMGUI_DEBUG_NAV_RECTS 0
942
943 // Visual Studio warnings
944 #ifdef _MSC_VER
945 #pragma warning (disable: 4127) // condition expression is constant
946 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
947 #endif
948
949 // Clang/GCC warnings with -Weverything
950 #ifdef __clang__
951 #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!
952 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
953 #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.
954 #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.
955 #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.
956 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
957 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
958 #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.
959 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
960 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
961 #if __has_warning("-Wdouble-promotion")
962 #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.
963 #endif
964 #elif defined(__GNUC__)
965 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
966 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
967 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
968 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
969 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
970 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
971 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
972 #if __GNUC__ >= 8
973 #pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
974 #endif
975 #endif
976
977 // 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.
978 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
979 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
980
981 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end)
982 static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow().
983 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
984
985 //-------------------------------------------------------------------------
986 // [SECTION] FORWARD DECLARATIONS
987 //-------------------------------------------------------------------------
988
989 static void SetCurrentWindow(ImGuiWindow* window);
990 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
991 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
992 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
993 static void FindHoveredWindow();
994 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
995 static void CheckStacksSize(ImGuiWindow* window, bool write);
996 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
997
998 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
999 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
1000 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1001
1002 static ImRect GetViewportRect();
1003
1004 // Settings
1005 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1006 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1007 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
1008
1009 // Platform Dependents default implementation for IO functions
1010 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
1011 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
1012 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
1013
1014 namespace ImGui
1015 {
1016 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
1017
1018 // Navigation
1019 static void NavUpdate();
1020 static void NavUpdateWindowing();
1021 static void NavUpdateWindowingList();
1022 static void NavUpdateMoveResult();
1023 static float NavUpdatePageUpPageDown(int allowed_dir_flags);
1024 static inline void NavUpdateAnyRequestFlag();
1025 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
1026 static ImVec2 NavCalcPreferredRefPos();
1027 static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
1028 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
1029
1030 // Misc
1031 static void UpdateMouseInputs();
1032 static void UpdateMouseWheel();
1033 static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
1034 }
1035
1036 //-----------------------------------------------------------------------------
1037 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
1038 //-----------------------------------------------------------------------------
1039
1040 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1041 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
1042 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
1043 // ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can:
1044 // - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1045 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
1046 #ifndef GImGui
1047 ImGuiContext* GImGui = NULL;
1048 #endif
1049
1050 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1051 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
1052 // 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.
1053 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)1054 static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size); }
FreeWrapper(void * ptr,void * user_data)1055 static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr); }
1056 #else
MallocWrapper(size_t size,void * user_data)1057 static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; (void)size; IM_ASSERT(0); return NULL; }
FreeWrapper(void * ptr,void * user_data)1058 static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); }
1059 #endif
1060
1061 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
1062 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
1063 static void* GImAllocatorUserData = NULL;
1064
1065 //-----------------------------------------------------------------------------
1066 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
1067 //-----------------------------------------------------------------------------
1068
ImGuiStyle()1069 ImGuiStyle::ImGuiStyle()
1070 {
1071 Alpha = 1.0f; // Global alpha applies to everything in ImGui
1072 WindowPadding = ImVec2(8,8); // Padding within a window
1073 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
1074 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1075 WindowMinSize = ImVec2(32,32); // Minimum window size
1076 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
1077 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1078 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1079 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1080 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1081 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
1082 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1083 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1084 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
1085 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1086 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!
1087 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1088 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
1089 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
1090 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1091 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
1092 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1093 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1094 TabBorderSize = 0.0f; // Thickness of border around tabs.
1095 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1096 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.
1097 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.
1098 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1099 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1100 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1101 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.
1102
1103 // Default theme
1104 ImGui::StyleColorsDark(this);
1105 }
1106
1107 // 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.
1108 // 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)1109 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1110 {
1111 WindowPadding = ImFloor(WindowPadding * scale_factor);
1112 WindowRounding = ImFloor(WindowRounding * scale_factor);
1113 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1114 ChildRounding = ImFloor(ChildRounding * scale_factor);
1115 PopupRounding = ImFloor(PopupRounding * scale_factor);
1116 FramePadding = ImFloor(FramePadding * scale_factor);
1117 FrameRounding = ImFloor(FrameRounding * scale_factor);
1118 TabRounding = ImFloor(TabRounding * scale_factor);
1119 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1120 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1121 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1122 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1123 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1124 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1125 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1126 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1127 GrabRounding = ImFloor(GrabRounding * scale_factor);
1128 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1129 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1130 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1131 }
1132
ImGuiIO()1133 ImGuiIO::ImGuiIO()
1134 {
1135 // Most fields are initialized with zero
1136 memset(this, 0, sizeof(*this));
1137
1138 // Settings
1139 ConfigFlags = ImGuiConfigFlags_None;
1140 BackendFlags = ImGuiBackendFlags_None;
1141 DisplaySize = ImVec2(-1.0f, -1.0f);
1142 DeltaTime = 1.0f/60.0f;
1143 IniSavingRate = 5.0f;
1144 IniFilename = "imgui.ini";
1145 LogFilename = "imgui_log.txt";
1146 MouseDoubleClickTime = 0.30f;
1147 MouseDoubleClickMaxDist = 6.0f;
1148 for (int i = 0; i < ImGuiKey_COUNT; i++)
1149 KeyMap[i] = -1;
1150 KeyRepeatDelay = 0.250f;
1151 KeyRepeatRate = 0.050f;
1152 UserData = NULL;
1153
1154 Fonts = NULL;
1155 FontGlobalScale = 1.0f;
1156 FontDefault = NULL;
1157 FontAllowUserScaling = false;
1158 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1159 DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
1160
1161 // Miscellaneous configuration options
1162 #ifdef __APPLE__
1163 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1164 #else
1165 ConfigMacOSXBehaviors = false;
1166 #endif
1167 ConfigInputTextCursorBlink = true;
1168 ConfigWindowsResizeFromEdges = true;
1169 ConfigWindowsMoveFromTitleBarOnly = false;
1170
1171 // Platform Functions
1172 BackendPlatformName = BackendRendererName = NULL;
1173 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1174 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1175 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1176 ClipboardUserData = NULL;
1177 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1178 ImeWindowHandle = NULL;
1179
1180 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1181 RenderDrawListsFn = NULL;
1182 #endif
1183
1184 // Input (NB: we already have memset zero the entire structure!)
1185 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1186 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1187 MouseDragThreshold = 6.0f;
1188 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1189 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1190 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1191 }
1192
1193 // Pass in translated ASCII characters for text input.
1194 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1195 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)1196 void ImGuiIO::AddInputCharacter(ImWchar c)
1197 {
1198 InputQueueCharacters.push_back(c);
1199 }
1200
AddInputCharactersUTF8(const char * utf8_chars)1201 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1202 {
1203 while (*utf8_chars != 0)
1204 {
1205 unsigned int c = 0;
1206 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1207 if (c > 0 && c <= 0xFFFF)
1208 InputQueueCharacters.push_back((ImWchar)c);
1209 }
1210 }
1211
ClearInputCharacters()1212 void ImGuiIO::ClearInputCharacters()
1213 {
1214 InputQueueCharacters.resize(0);
1215 }
1216
1217 //-----------------------------------------------------------------------------
1218 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1219 //-----------------------------------------------------------------------------
1220
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1221 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1222 {
1223 ImVec2 ap = p - a;
1224 ImVec2 ab_dir = b - a;
1225 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1226 if (dot < 0.0f)
1227 return a;
1228 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1229 if (dot > ab_len_sqr)
1230 return b;
1231 return a + ab_dir * dot / ab_len_sqr;
1232 }
1233
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1234 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1235 {
1236 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1237 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1238 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1239 return ((b1 == b2) && (b2 == b3));
1240 }
1241
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1242 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1243 {
1244 ImVec2 v0 = b - a;
1245 ImVec2 v1 = c - a;
1246 ImVec2 v2 = p - a;
1247 const float denom = v0.x * v1.y - v1.x * v0.y;
1248 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1249 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1250 out_u = 1.0f - out_v - out_w;
1251 }
1252
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1253 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1254 {
1255 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1256 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1257 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1258 float dist2_ab = ImLengthSqr(p - proj_ab);
1259 float dist2_bc = ImLengthSqr(p - proj_bc);
1260 float dist2_ca = ImLengthSqr(p - proj_ca);
1261 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1262 if (m == dist2_ab)
1263 return proj_ab;
1264 if (m == dist2_bc)
1265 return proj_bc;
1266 return proj_ca;
1267 }
1268
ImStricmp(const char * str1,const char * str2)1269 int ImStricmp(const char* str1, const char* str2)
1270 {
1271 int d;
1272 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1273 return d;
1274 }
1275
ImStrnicmp(const char * str1,const char * str2,size_t count)1276 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1277 {
1278 int d = 0;
1279 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1280 return d;
1281 }
1282
ImStrncpy(char * dst,const char * src,size_t count)1283 void ImStrncpy(char* dst, const char* src, size_t count)
1284 {
1285 if (count < 1) return;
1286 strncpy(dst, src, count);
1287 dst[count-1] = 0;
1288 }
1289
ImStrdup(const char * str)1290 char* ImStrdup(const char* str)
1291 {
1292 size_t len = strlen(str);
1293 void* buf = ImGui::MemAlloc(len + 1);
1294 return (char*)memcpy(buf, (const void*)str, len + 1);
1295 }
1296
ImStrdupcpy(char * dst,size_t * p_dst_size,const char * src)1297 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
1298 {
1299 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;
1300 size_t src_size = strlen(src) + 1;
1301 if (dst_buf_size < src_size)
1302 {
1303 ImGui::MemFree(dst);
1304 dst = (char*)ImGui::MemAlloc(src_size);
1305 if (p_dst_size)
1306 *p_dst_size = src_size;
1307 }
1308 return (char*)memcpy(dst, (const void*)src, src_size);
1309 }
1310
ImStrchrRange(const char * str,const char * str_end,char c)1311 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1312 {
1313 const char* p = (const char*)memchr(str, (int)c, str_end - str);
1314 return p;
1315 }
1316
ImStrlenW(const ImWchar * str)1317 int ImStrlenW(const ImWchar* str)
1318 {
1319 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bits
1320 int n = 0;
1321 while (*str++) n++;
1322 return n;
1323 }
1324
1325 // Find end-of-line. Return pointer will point to either first \n, either str_end.
ImStreolRange(const char * str,const char * str_end)1326 const char* ImStreolRange(const char* str, const char* str_end)
1327 {
1328 const char* p = (const char*)memchr(str, '\n', str_end - str);
1329 return p ? p : str_end;
1330 }
1331
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1332 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1333 {
1334 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1335 buf_mid_line--;
1336 return buf_mid_line;
1337 }
1338
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1339 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1340 {
1341 if (!needle_end)
1342 needle_end = needle + strlen(needle);
1343
1344 const char un0 = (char)toupper(*needle);
1345 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1346 {
1347 if (toupper(*haystack) == un0)
1348 {
1349 const char* b = needle + 1;
1350 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1351 if (toupper(*a) != toupper(*b))
1352 break;
1353 if (b == needle_end)
1354 return haystack;
1355 }
1356 haystack++;
1357 }
1358 return NULL;
1359 }
1360
1361 // 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)1362 void ImStrTrimBlanks(char* buf)
1363 {
1364 char* p = buf;
1365 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1366 p++;
1367 char* p_start = p;
1368 while (*p != 0) // Find end of string
1369 p++;
1370 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1371 p--;
1372 if (p_start != buf) // Copy memory if we had leading blanks
1373 memmove(buf, p_start, p - p_start);
1374 buf[p - p_start] = 0; // Zero terminate
1375 }
1376
1377 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1378 // 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.
1379 // B) When buf==NULL vsnprintf() will return the output size.
1380 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1381
1382 //#define IMGUI_USE_STB_SPRINTF
1383 #ifdef IMGUI_USE_STB_SPRINTF
1384 #define STB_SPRINTF_IMPLEMENTATION
1385 #include "imstb_sprintf.h"
1386 #endif
1387
1388 #if defined(_MSC_VER) && !defined(vsnprintf)
1389 #define vsnprintf _vsnprintf
1390 #endif
1391
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1392 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1393 {
1394 va_list args;
1395 va_start(args, fmt);
1396 #ifdef IMGUI_USE_STB_SPRINTF
1397 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1398 #else
1399 int w = vsnprintf(buf, buf_size, fmt, args);
1400 #endif
1401 va_end(args);
1402 if (buf == NULL)
1403 return w;
1404 if (w == -1 || w >= (int)buf_size)
1405 w = (int)buf_size - 1;
1406 buf[w] = 0;
1407 return w;
1408 }
1409
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1410 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1411 {
1412 #ifdef IMGUI_USE_STB_SPRINTF
1413 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
1414 #else
1415 int w = vsnprintf(buf, buf_size, fmt, args);
1416 #endif
1417 if (buf == NULL)
1418 return w;
1419 if (w == -1 || w >= (int)buf_size)
1420 w = (int)buf_size - 1;
1421 buf[w] = 0;
1422 return w;
1423 }
1424 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1425
1426 // Pass data_size == 0 for zero-terminated strings, data_size > 0 for non-string data.
1427 // Pay attention that data_size==0 will yield different results than passing strlen(data) because the zero-terminated codepath handles ###.
1428 // This should technically be split into two distinct functions (ImHashData/ImHashStr), perhaps once we remove the silly static variable.
1429 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImHash(const void * data,int data_size,ImU32 seed)1430 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1431 {
1432 static ImU32 crc32_lut[256] = { 0 };
1433 if (!crc32_lut[1])
1434 {
1435 const ImU32 polynomial = 0xEDB88320;
1436 for (ImU32 i = 0; i < 256; i++)
1437 {
1438 ImU32 crc = i;
1439 for (ImU32 j = 0; j < 8; j++)
1440 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1441 crc32_lut[i] = crc;
1442 }
1443 }
1444
1445 seed = ~seed;
1446 ImU32 crc = seed;
1447 const unsigned char* current = (const unsigned char*)data;
1448
1449 if (data_size > 0)
1450 {
1451 // Known size
1452 while (data_size--)
1453 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1454 }
1455 else
1456 {
1457 // Zero-terminated string
1458 while (unsigned char c = *current++)
1459 {
1460 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1461 // Because this syntax is rarely used we are optimizing for the common case.
1462 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1463 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1464 if (c == '#' && current[0] == '#' && current[1] == '#')
1465 crc = seed;
1466 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1467 }
1468 }
1469 return ~crc;
1470 }
1471
ImFileOpen(const char * filename,const char * mode)1472 FILE* ImFileOpen(const char* filename, const char* mode)
1473 {
1474 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1475 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can)
1476 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1477 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1478 ImVector<ImWchar> buf;
1479 buf.resize(filename_wsize + mode_wsize);
1480 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1481 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1482 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1483 #else
1484 return fopen(filename, mode);
1485 #endif
1486 }
1487
1488 // Load file content into memory
1489 // Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree()
ImFileLoadToMemory(const char * filename,const char * file_open_mode,size_t * out_file_size,int padding_bytes)1490 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1491 {
1492 IM_ASSERT(filename && file_open_mode);
1493 if (out_file_size)
1494 *out_file_size = 0;
1495
1496 FILE* f;
1497 if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1498 return NULL;
1499
1500 long file_size_signed;
1501 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1502 {
1503 fclose(f);
1504 return NULL;
1505 }
1506
1507 size_t file_size = (size_t)file_size_signed;
1508 void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1509 if (file_data == NULL)
1510 {
1511 fclose(f);
1512 return NULL;
1513 }
1514 if (fread(file_data, 1, file_size, f) != file_size)
1515 {
1516 fclose(f);
1517 ImGui::MemFree(file_data);
1518 return NULL;
1519 }
1520 if (padding_bytes > 0)
1521 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1522
1523 fclose(f);
1524 if (out_file_size)
1525 *out_file_size = file_size;
1526
1527 return file_data;
1528 }
1529
1530 //-----------------------------------------------------------------------------
1531 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1532 //-----------------------------------------------------------------------------
1533
1534 // Convert UTF-8 to 32-bits character, process single character input.
1535 // Based on stb_from_utf8() from github.com/nothings/stb/
1536 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1537 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1538 {
1539 unsigned int c = (unsigned int)-1;
1540 const unsigned char* str = (const unsigned char*)in_text;
1541 if (!(*str & 0x80))
1542 {
1543 c = (unsigned int)(*str++);
1544 *out_char = c;
1545 return 1;
1546 }
1547 if ((*str & 0xe0) == 0xc0)
1548 {
1549 *out_char = 0xFFFD; // will be invalid but not end of string
1550 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1551 if (*str < 0xc2) return 2;
1552 c = (unsigned int)((*str++ & 0x1f) << 6);
1553 if ((*str & 0xc0) != 0x80) return 2;
1554 c += (*str++ & 0x3f);
1555 *out_char = c;
1556 return 2;
1557 }
1558 if ((*str & 0xf0) == 0xe0)
1559 {
1560 *out_char = 0xFFFD; // will be invalid but not end of string
1561 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1562 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1563 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1564 c = (unsigned int)((*str++ & 0x0f) << 12);
1565 if ((*str & 0xc0) != 0x80) return 3;
1566 c += (unsigned int)((*str++ & 0x3f) << 6);
1567 if ((*str & 0xc0) != 0x80) return 3;
1568 c += (*str++ & 0x3f);
1569 *out_char = c;
1570 return 3;
1571 }
1572 if ((*str & 0xf8) == 0xf0)
1573 {
1574 *out_char = 0xFFFD; // will be invalid but not end of string
1575 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1576 if (*str > 0xf4) return 4;
1577 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1578 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1579 c = (unsigned int)((*str++ & 0x07) << 18);
1580 if ((*str & 0xc0) != 0x80) return 4;
1581 c += (unsigned int)((*str++ & 0x3f) << 12);
1582 if ((*str & 0xc0) != 0x80) return 4;
1583 c += (unsigned int)((*str++ & 0x3f) << 6);
1584 if ((*str & 0xc0) != 0x80) return 4;
1585 c += (*str++ & 0x3f);
1586 // utf-8 encodings of values used in surrogate pairs are invalid
1587 if ((c & 0xFFFFF800) == 0xD800) return 4;
1588 *out_char = c;
1589 return 4;
1590 }
1591 *out_char = 0;
1592 return 0;
1593 }
1594
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1595 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1596 {
1597 ImWchar* buf_out = buf;
1598 ImWchar* buf_end = buf + buf_size;
1599 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1600 {
1601 unsigned int c;
1602 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1603 if (c == 0)
1604 break;
1605 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1606 *buf_out++ = (ImWchar)c;
1607 }
1608 *buf_out = 0;
1609 if (in_text_remaining)
1610 *in_text_remaining = in_text;
1611 return (int)(buf_out - buf);
1612 }
1613
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1614 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1615 {
1616 int char_count = 0;
1617 while ((!in_text_end || in_text < in_text_end) && *in_text)
1618 {
1619 unsigned int c;
1620 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1621 if (c == 0)
1622 break;
1623 if (c < 0x10000)
1624 char_count++;
1625 }
1626 return char_count;
1627 }
1628
1629 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1630 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1631 {
1632 if (c < 0x80)
1633 {
1634 buf[0] = (char)c;
1635 return 1;
1636 }
1637 if (c < 0x800)
1638 {
1639 if (buf_size < 2) return 0;
1640 buf[0] = (char)(0xc0 + (c >> 6));
1641 buf[1] = (char)(0x80 + (c & 0x3f));
1642 return 2;
1643 }
1644 if (c >= 0xdc00 && c < 0xe000)
1645 {
1646 return 0;
1647 }
1648 if (c >= 0xd800 && c < 0xdc00)
1649 {
1650 if (buf_size < 4) return 0;
1651 buf[0] = (char)(0xf0 + (c >> 18));
1652 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1653 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1654 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1655 return 4;
1656 }
1657 //else if (c < 0x10000)
1658 {
1659 if (buf_size < 3) return 0;
1660 buf[0] = (char)(0xe0 + (c >> 12));
1661 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1662 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1663 return 3;
1664 }
1665 }
1666
1667 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1668 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1669 {
1670 unsigned int dummy = 0;
1671 return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1672 }
1673
ImTextCountUtf8BytesFromChar(unsigned int c)1674 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1675 {
1676 if (c < 0x80) return 1;
1677 if (c < 0x800) return 2;
1678 if (c >= 0xdc00 && c < 0xe000) return 0;
1679 if (c >= 0xd800 && c < 0xdc00) return 4;
1680 return 3;
1681 }
1682
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1683 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1684 {
1685 char* buf_out = buf;
1686 const char* buf_end = buf + buf_size;
1687 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1688 {
1689 unsigned int c = (unsigned int)(*in_text++);
1690 if (c < 0x80)
1691 *buf_out++ = (char)c;
1692 else
1693 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1694 }
1695 *buf_out = 0;
1696 return (int)(buf_out - buf);
1697 }
1698
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1699 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1700 {
1701 int bytes_count = 0;
1702 while ((!in_text_end || in_text < in_text_end) && *in_text)
1703 {
1704 unsigned int c = (unsigned int)(*in_text++);
1705 if (c < 0x80)
1706 bytes_count++;
1707 else
1708 bytes_count += ImTextCountUtf8BytesFromChar(c);
1709 }
1710 return bytes_count;
1711 }
1712
1713 //-----------------------------------------------------------------------------
1714 // [SECTION] MISC HELPER/UTILTIES (Color functions)
1715 // Note: The Convert functions are early design which are not consistent with other API.
1716 //-----------------------------------------------------------------------------
1717
ColorConvertU32ToFloat4(ImU32 in)1718 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1719 {
1720 float s = 1.0f/255.0f;
1721 return ImVec4(
1722 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1723 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1724 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1725 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1726 }
1727
ColorConvertFloat4ToU32(const ImVec4 & in)1728 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1729 {
1730 ImU32 out;
1731 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1732 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1733 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1734 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1735 return out;
1736 }
1737
1738 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1739 // 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)1740 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1741 {
1742 float K = 0.f;
1743 if (g < b)
1744 {
1745 ImSwap(g, b);
1746 K = -1.f;
1747 }
1748 if (r < g)
1749 {
1750 ImSwap(r, g);
1751 K = -2.f / 6.f - K;
1752 }
1753
1754 const float chroma = r - (g < b ? g : b);
1755 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1756 out_s = chroma / (r + 1e-20f);
1757 out_v = r;
1758 }
1759
1760 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1761 // 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)1762 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1763 {
1764 if (s == 0.0f)
1765 {
1766 // gray
1767 out_r = out_g = out_b = v;
1768 return;
1769 }
1770
1771 h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1772 int i = (int)h;
1773 float f = h - (float)i;
1774 float p = v * (1.0f - s);
1775 float q = v * (1.0f - s * f);
1776 float t = v * (1.0f - s * (1.0f - f));
1777
1778 switch (i)
1779 {
1780 case 0: out_r = v; out_g = t; out_b = p; break;
1781 case 1: out_r = q; out_g = v; out_b = p; break;
1782 case 2: out_r = p; out_g = v; out_b = t; break;
1783 case 3: out_r = p; out_g = q; out_b = v; break;
1784 case 4: out_r = t; out_g = p; out_b = v; break;
1785 case 5: default: out_r = v; out_g = p; out_b = q; break;
1786 }
1787 }
1788
GetColorU32(ImGuiCol idx,float alpha_mul)1789 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1790 {
1791 ImGuiStyle& style = GImGui->Style;
1792 ImVec4 c = style.Colors[idx];
1793 c.w *= style.Alpha * alpha_mul;
1794 return ColorConvertFloat4ToU32(c);
1795 }
1796
GetColorU32(const ImVec4 & col)1797 ImU32 ImGui::GetColorU32(const ImVec4& col)
1798 {
1799 ImGuiStyle& style = GImGui->Style;
1800 ImVec4 c = col;
1801 c.w *= style.Alpha;
1802 return ColorConvertFloat4ToU32(c);
1803 }
1804
GetStyleColorVec4(ImGuiCol idx)1805 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1806 {
1807 ImGuiStyle& style = GImGui->Style;
1808 return style.Colors[idx];
1809 }
1810
GetColorU32(ImU32 col)1811 ImU32 ImGui::GetColorU32(ImU32 col)
1812 {
1813 float style_alpha = GImGui->Style.Alpha;
1814 if (style_alpha >= 1.0f)
1815 return col;
1816 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1817 a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1818 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1819 }
1820
1821 //-----------------------------------------------------------------------------
1822 // [SECTION] ImGuiStorage
1823 // Helper: Key->value storage
1824 //-----------------------------------------------------------------------------
1825
1826 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1827 static ImGuiStorage::Pair* LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1828 {
1829 ImGuiStorage::Pair* first = data.Data;
1830 ImGuiStorage::Pair* last = data.Data + data.Size;
1831 size_t count = (size_t)(last - first);
1832 while (count > 0)
1833 {
1834 size_t count2 = count >> 1;
1835 ImGuiStorage::Pair* mid = first + count2;
1836 if (mid->key < key)
1837 {
1838 first = ++mid;
1839 count -= count2 + 1;
1840 }
1841 else
1842 {
1843 count = count2;
1844 }
1845 }
1846 return first;
1847 }
1848
1849 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1850 void ImGuiStorage::BuildSortByKey()
1851 {
1852 struct StaticFunc
1853 {
1854 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1855 {
1856 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1857 if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1858 if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1859 return 0;
1860 }
1861 };
1862 if (Data.Size > 1)
1863 ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1864 }
1865
GetInt(ImGuiID key,int default_val) const1866 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1867 {
1868 ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1869 if (it == Data.end() || it->key != key)
1870 return default_val;
1871 return it->val_i;
1872 }
1873
GetBool(ImGuiID key,bool default_val) const1874 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1875 {
1876 return GetInt(key, default_val ? 1 : 0) != 0;
1877 }
1878
GetFloat(ImGuiID key,float default_val) const1879 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1880 {
1881 ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1882 if (it == Data.end() || it->key != key)
1883 return default_val;
1884 return it->val_f;
1885 }
1886
GetVoidPtr(ImGuiID key) const1887 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1888 {
1889 ImGuiStorage::Pair* it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1890 if (it == Data.end() || it->key != key)
1891 return NULL;
1892 return it->val_p;
1893 }
1894
1895 // 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)1896 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1897 {
1898 ImGuiStorage::Pair* it = LowerBound(Data, key);
1899 if (it == Data.end() || it->key != key)
1900 it = Data.insert(it, Pair(key, default_val));
1901 return &it->val_i;
1902 }
1903
GetBoolRef(ImGuiID key,bool default_val)1904 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1905 {
1906 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1907 }
1908
GetFloatRef(ImGuiID key,float default_val)1909 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1910 {
1911 ImGuiStorage::Pair* it = LowerBound(Data, key);
1912 if (it == Data.end() || it->key != key)
1913 it = Data.insert(it, Pair(key, default_val));
1914 return &it->val_f;
1915 }
1916
GetVoidPtrRef(ImGuiID key,void * default_val)1917 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1918 {
1919 ImGuiStorage::Pair* it = LowerBound(Data, key);
1920 if (it == Data.end() || it->key != key)
1921 it = Data.insert(it, Pair(key, default_val));
1922 return &it->val_p;
1923 }
1924
1925 // 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)1926 void ImGuiStorage::SetInt(ImGuiID key, int val)
1927 {
1928 ImGuiStorage::Pair* it = LowerBound(Data, key);
1929 if (it == Data.end() || it->key != key)
1930 {
1931 Data.insert(it, Pair(key, val));
1932 return;
1933 }
1934 it->val_i = val;
1935 }
1936
SetBool(ImGuiID key,bool val)1937 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1938 {
1939 SetInt(key, val ? 1 : 0);
1940 }
1941
SetFloat(ImGuiID key,float val)1942 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1943 {
1944 ImGuiStorage::Pair* it = LowerBound(Data, key);
1945 if (it == Data.end() || it->key != key)
1946 {
1947 Data.insert(it, Pair(key, val));
1948 return;
1949 }
1950 it->val_f = val;
1951 }
1952
SetVoidPtr(ImGuiID key,void * val)1953 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1954 {
1955 ImGuiStorage::Pair* it = LowerBound(Data, key);
1956 if (it == Data.end() || it->key != key)
1957 {
1958 Data.insert(it, Pair(key, val));
1959 return;
1960 }
1961 it->val_p = val;
1962 }
1963
SetAllInt(int v)1964 void ImGuiStorage::SetAllInt(int v)
1965 {
1966 for (int i = 0; i < Data.Size; i++)
1967 Data[i].val_i = v;
1968 }
1969
1970 //-----------------------------------------------------------------------------
1971 // [SECTION] ImGuiTextFilter
1972 //-----------------------------------------------------------------------------
1973
1974 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1975 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1976 {
1977 if (default_filter)
1978 {
1979 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1980 Build();
1981 }
1982 else
1983 {
1984 InputBuf[0] = 0;
1985 CountGrep = 0;
1986 }
1987 }
1988
Draw(const char * label,float width)1989 bool ImGuiTextFilter::Draw(const char* label, float width)
1990 {
1991 if (width != 0.0f)
1992 ImGui::PushItemWidth(width);
1993 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1994 if (width != 0.0f)
1995 ImGui::PopItemWidth();
1996 if (value_changed)
1997 Build();
1998 return value_changed;
1999 }
2000
split(char separator,ImVector<TextRange> * out) const2001 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
2002 {
2003 out->resize(0);
2004 const char* wb = b;
2005 const char* we = wb;
2006 while (we < e)
2007 {
2008 if (*we == separator)
2009 {
2010 out->push_back(TextRange(wb, we));
2011 wb = we + 1;
2012 }
2013 we++;
2014 }
2015 if (wb != we)
2016 out->push_back(TextRange(wb, we));
2017 }
2018
Build()2019 void ImGuiTextFilter::Build()
2020 {
2021 Filters.resize(0);
2022 TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
2023 input_range.split(',', &Filters);
2024
2025 CountGrep = 0;
2026 for (int i = 0; i != Filters.Size; i++)
2027 {
2028 TextRange& f = Filters[i];
2029 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2030 f.b++;
2031 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2032 f.e--;
2033 if (f.empty())
2034 continue;
2035 if (Filters[i].b[0] != '-')
2036 CountGrep += 1;
2037 }
2038 }
2039
PassFilter(const char * text,const char * text_end) const2040 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2041 {
2042 if (Filters.empty())
2043 return true;
2044
2045 if (text == NULL)
2046 text = "";
2047
2048 for (int i = 0; i != Filters.Size; i++)
2049 {
2050 const TextRange& f = Filters[i];
2051 if (f.empty())
2052 continue;
2053 if (f.b[0] == '-')
2054 {
2055 // Subtract
2056 if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
2057 return false;
2058 }
2059 else
2060 {
2061 // Grep
2062 if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
2063 return true;
2064 }
2065 }
2066
2067 // Implicit * grep
2068 if (CountGrep == 0)
2069 return true;
2070
2071 return false;
2072 }
2073
2074 //-----------------------------------------------------------------------------
2075 // [SECTION] ImGuiTextBuffer
2076 //-----------------------------------------------------------------------------
2077
2078 // On some platform vsnprintf() takes va_list by reference and modifies it.
2079 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
2080 #ifndef va_copy
2081 #if defined(__GNUC__) || defined(__clang__)
2082 #define va_copy(dest, src) __builtin_va_copy(dest, src)
2083 #else
2084 #define va_copy(dest, src) (dest = src)
2085 #endif
2086 #endif
2087
2088 char ImGuiTextBuffer::EmptyString[1] = { 0 };
2089
2090 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)2091 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
2092 {
2093 va_list args_copy;
2094 va_copy(args_copy, args);
2095
2096 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
2097 if (len <= 0)
2098 {
2099 va_end(args_copy);
2100 return;
2101 }
2102
2103 // Add zero-terminator the first time
2104 const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
2105 const int needed_sz = write_off + len;
2106 if (write_off + len >= Buf.Capacity)
2107 {
2108 int double_capacity = Buf.Capacity * 2;
2109 Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
2110 }
2111
2112 Buf.resize(needed_sz);
2113 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
2114 va_end(args_copy);
2115 }
2116
appendf(const char * fmt,...)2117 void ImGuiTextBuffer::appendf(const char* fmt, ...)
2118 {
2119 va_list args;
2120 va_start(args, fmt);
2121 appendfv(fmt, args);
2122 va_end(args);
2123 }
2124
2125 //-----------------------------------------------------------------------------
2126 // [SECTION] ImGuiListClipper
2127 // This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
2128 //-----------------------------------------------------------------------------
2129
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)2130 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
2131 {
2132 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
2133 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
2134 // The clipper should probably have a 4th step to display the last item in a regular manner.
2135 ImGui::SetCursorPosY(pos_y);
2136 ImGuiWindow* window = ImGui::GetCurrentWindow();
2137 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.
2138 window->DC.PrevLineSize.y = (line_height - GImGui->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.
2139 if (window->DC.ColumnsSet)
2140 window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2141 }
2142
2143 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2144 // Use case B: Begin() called from constructor with items_height>0
2145 // 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)2146 void ImGuiListClipper::Begin(int count, float items_height)
2147 {
2148 StartPosY = ImGui::GetCursorPosY();
2149 ItemsHeight = items_height;
2150 ItemsCount = count;
2151 StepNo = 0;
2152 DisplayEnd = DisplayStart = -1;
2153 if (ItemsHeight > 0.0f)
2154 {
2155 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2156 if (DisplayStart > 0)
2157 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2158 StepNo = 2;
2159 }
2160 }
2161
End()2162 void ImGuiListClipper::End()
2163 {
2164 if (ItemsCount < 0)
2165 return;
2166 // 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.
2167 if (ItemsCount < INT_MAX)
2168 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2169 ItemsCount = -1;
2170 StepNo = 3;
2171 }
2172
Step()2173 bool ImGuiListClipper::Step()
2174 {
2175 if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2176 {
2177 ItemsCount = -1;
2178 return false;
2179 }
2180 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.
2181 {
2182 DisplayStart = 0;
2183 DisplayEnd = 1;
2184 StartPosY = ImGui::GetCursorPosY();
2185 StepNo = 1;
2186 return true;
2187 }
2188 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.
2189 {
2190 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2191 float items_height = ImGui::GetCursorPosY() - StartPosY;
2192 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2193 Begin(ItemsCount-1, items_height);
2194 DisplayStart++;
2195 DisplayEnd++;
2196 StepNo = 3;
2197 return true;
2198 }
2199 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.
2200 {
2201 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2202 StepNo = 3;
2203 return true;
2204 }
2205 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.
2206 End();
2207 return false;
2208 }
2209
2210 //-----------------------------------------------------------------------------
2211 // [SECTION] RENDER HELPERS
2212 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2213 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2214 //-----------------------------------------------------------------------------
2215
FindRenderedTextEnd(const char * text,const char * text_end)2216 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2217 {
2218 const char* text_display_end = text;
2219 if (!text_end)
2220 text_end = (const char*)-1;
2221
2222 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2223 text_display_end++;
2224 return text_display_end;
2225 }
2226
2227 // Internal ImGui functions to render text
2228 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2229 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2230 {
2231 ImGuiContext& g = *GImGui;
2232 ImGuiWindow* window = g.CurrentWindow;
2233
2234 // Hide anything after a '##' string
2235 const char* text_display_end;
2236 if (hide_text_after_hash)
2237 {
2238 text_display_end = FindRenderedTextEnd(text, text_end);
2239 }
2240 else
2241 {
2242 if (!text_end)
2243 text_end = text + strlen(text); // FIXME-OPT
2244 text_display_end = text_end;
2245 }
2246
2247 if (text != text_display_end)
2248 {
2249 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2250 if (g.LogEnabled)
2251 LogRenderedText(&pos, text, text_display_end);
2252 }
2253 }
2254
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2255 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2256 {
2257 ImGuiContext& g = *GImGui;
2258 ImGuiWindow* window = g.CurrentWindow;
2259
2260 if (!text_end)
2261 text_end = text + strlen(text); // FIXME-OPT
2262
2263 if (text != text_end)
2264 {
2265 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2266 if (g.LogEnabled)
2267 LogRenderedText(&pos, text, text_end);
2268 }
2269 }
2270
2271 // Default clip_rect uses (pos_min,pos_max)
2272 // 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)2273 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)
2274 {
2275 // Perform CPU side clipping for single clipped element to avoid using scissor state
2276 ImVec2 pos = pos_min;
2277 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2278
2279 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2280 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2281 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2282 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2283 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2284
2285 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2286 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2287 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2288
2289 // Render
2290 if (need_clipping)
2291 {
2292 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2293 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2294 }
2295 else
2296 {
2297 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2298 }
2299 }
2300
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)2301 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)
2302 {
2303 // Hide anything after a '##' string
2304 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2305 const int text_len = (int)(text_display_end - text);
2306 if (text_len == 0)
2307 return;
2308
2309 ImGuiContext& g = *GImGui;
2310 ImGuiWindow* window = g.CurrentWindow;
2311 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
2312 if (g.LogEnabled)
2313 LogRenderedText(&pos_min, text, text_display_end);
2314 }
2315
2316 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2317 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2318 {
2319 ImGuiContext& g = *GImGui;
2320 ImGuiWindow* window = g.CurrentWindow;
2321 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2322 const float border_size = g.Style.FrameBorderSize;
2323 if (border && border_size > 0.0f)
2324 {
2325 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2326 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2327 }
2328 }
2329
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2330 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2331 {
2332 ImGuiContext& g = *GImGui;
2333 ImGuiWindow* window = g.CurrentWindow;
2334 const float border_size = g.Style.FrameBorderSize;
2335 if (border_size > 0.0f)
2336 {
2337 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2338 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2339 }
2340 }
2341
2342 // 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(ImVec2 p_min,ImGuiDir dir,float scale)2343 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2344 {
2345 ImGuiContext& g = *GImGui;
2346
2347 const float h = g.FontSize * 1.00f;
2348 float r = h * 0.40f * scale;
2349 ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2350
2351 ImVec2 a, b, c;
2352 switch (dir)
2353 {
2354 case ImGuiDir_Up:
2355 case ImGuiDir_Down:
2356 if (dir == ImGuiDir_Up) r = -r;
2357 a = ImVec2(+0.000f,+0.750f) * r;
2358 b = ImVec2(-0.866f,-0.750f) * r;
2359 c = ImVec2(+0.866f,-0.750f) * r;
2360 break;
2361 case ImGuiDir_Left:
2362 case ImGuiDir_Right:
2363 if (dir == ImGuiDir_Left) r = -r;
2364 a = ImVec2(+0.750f,+0.000f) * r;
2365 b = ImVec2(-0.750f,+0.866f) * r;
2366 c = ImVec2(-0.750f,-0.866f) * r;
2367 break;
2368 case ImGuiDir_None:
2369 case ImGuiDir_COUNT:
2370 IM_ASSERT(0);
2371 break;
2372 }
2373
2374 g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2375 }
2376
RenderBullet(ImVec2 pos)2377 void ImGui::RenderBullet(ImVec2 pos)
2378 {
2379 ImGuiContext& g = *GImGui;
2380 ImGuiWindow* window = g.CurrentWindow;
2381 window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2382 }
2383
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2384 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2385 {
2386 ImGuiContext& g = *GImGui;
2387 ImGuiWindow* window = g.CurrentWindow;
2388
2389 float thickness = ImMax(sz / 5.0f, 1.0f);
2390 sz -= thickness*0.5f;
2391 pos += ImVec2(thickness*0.25f, thickness*0.25f);
2392
2393 float third = sz / 3.0f;
2394 float bx = pos.x + third;
2395 float by = pos.y + sz - third*0.5f;
2396 window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2397 window->DrawList->PathLineTo(ImVec2(bx, by));
2398 window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2399 window->DrawList->PathStroke(col, false, thickness);
2400 }
2401
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2402 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2403 {
2404 ImGuiContext& g = *GImGui;
2405 if (id != g.NavId)
2406 return;
2407 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2408 return;
2409 ImGuiWindow* window = g.CurrentWindow;
2410 if (window->DC.NavHideHighlightOneFrame)
2411 return;
2412
2413 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2414 ImRect display_rect = bb;
2415 display_rect.ClipWith(window->ClipRect);
2416 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2417 {
2418 const float THICKNESS = 2.0f;
2419 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2420 display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2421 bool fully_visible = window->ClipRect.Contains(display_rect);
2422 if (!fully_visible)
2423 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2424 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);
2425 if (!fully_visible)
2426 window->DrawList->PopClipRect();
2427 }
2428 if (flags & ImGuiNavHighlightFlags_TypeThin)
2429 {
2430 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2431 }
2432 }
2433
2434 //-----------------------------------------------------------------------------
2435 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2436 //-----------------------------------------------------------------------------
2437
2438 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2439 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2440 : DrawListInst(&context->DrawListSharedData)
2441 {
2442 Name = ImStrdup(name);
2443 ID = ImHash(name, 0);
2444 IDStack.push_back(ID);
2445 Flags = ImGuiWindowFlags_None;
2446 Pos = ImVec2(0.0f, 0.0f);
2447 Size = SizeFull = ImVec2(0.0f, 0.0f);
2448 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2449 WindowPadding = ImVec2(0.0f, 0.0f);
2450 WindowRounding = 0.0f;
2451 WindowBorderSize = 0.0f;
2452 NameBufLen = (int)strlen(name) + 1;
2453 MoveId = GetID("#MOVE");
2454 ChildId = 0;
2455 Scroll = ImVec2(0.0f, 0.0f);
2456 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2457 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2458 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2459 ScrollbarX = ScrollbarY = false;
2460 Active = WasActive = false;
2461 WriteAccessed = false;
2462 Collapsed = false;
2463 WantCollapseToggle = false;
2464 SkipItems = false;
2465 Appearing = false;
2466 Hidden = false;
2467 HasCloseButton = false;
2468 BeginCount = 0;
2469 BeginOrderWithinParent = -1;
2470 BeginOrderWithinContext = -1;
2471 PopupId = 0;
2472 AutoFitFramesX = AutoFitFramesY = -1;
2473 AutoFitOnlyGrows = false;
2474 AutoFitChildAxises = 0x00;
2475 AutoPosLastDirection = ImGuiDir_None;
2476 HiddenFramesRegular = HiddenFramesForResize = 0;
2477 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2478 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2479
2480 LastFrameActive = -1;
2481 ItemWidthDefault = 0.0f;
2482 FontWindowScale = 1.0f;
2483 SettingsIdx = -1;
2484
2485 DrawList = &DrawListInst;
2486 DrawList->_OwnerName = Name;
2487 ParentWindow = NULL;
2488 RootWindow = NULL;
2489 RootWindowForTitleBarHighlight = NULL;
2490 RootWindowForNav = NULL;
2491
2492 NavLastIds[0] = NavLastIds[1] = 0;
2493 NavRectRel[0] = NavRectRel[1] = ImRect();
2494 NavLastChildNavWindow = NULL;
2495
2496 FocusIdxAllCounter = FocusIdxTabCounter = -1;
2497 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2498 FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2499 }
2500
~ImGuiWindow()2501 ImGuiWindow::~ImGuiWindow()
2502 {
2503 IM_ASSERT(DrawList == &DrawListInst);
2504 IM_DELETE(Name);
2505 for (int i = 0; i != ColumnsStorage.Size; i++)
2506 ColumnsStorage[i].~ImGuiColumnsSet();
2507 }
2508
GetID(const char * str,const char * str_end)2509 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2510 {
2511 ImGuiID seed = IDStack.back();
2512 ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2513 ImGui::KeepAliveID(id);
2514 return id;
2515 }
2516
GetID(const void * ptr)2517 ImGuiID ImGuiWindow::GetID(const void* ptr)
2518 {
2519 ImGuiID seed = IDStack.back();
2520 ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2521 ImGui::KeepAliveID(id);
2522 return id;
2523 }
2524
GetIDNoKeepAlive(const char * str,const char * str_end)2525 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2526 {
2527 ImGuiID seed = IDStack.back();
2528 return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2529 }
2530
GetIDNoKeepAlive(const void * ptr)2531 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2532 {
2533 ImGuiID seed = IDStack.back();
2534 return ImHash(&ptr, sizeof(void*), seed);
2535 }
2536
2537 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2538 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2539 {
2540 ImGuiID seed = IDStack.back();
2541 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) };
2542 ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2543 ImGui::KeepAliveID(id);
2544 return id;
2545 }
2546
SetCurrentWindow(ImGuiWindow * window)2547 static void SetCurrentWindow(ImGuiWindow* window)
2548 {
2549 ImGuiContext& g = *GImGui;
2550 g.CurrentWindow = window;
2551 if (window)
2552 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2553 }
2554
SetNavID(ImGuiID id,int nav_layer)2555 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2556 {
2557 ImGuiContext& g = *GImGui;
2558 IM_ASSERT(g.NavWindow);
2559 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2560 g.NavId = id;
2561 g.NavWindow->NavLastIds[nav_layer] = id;
2562 }
2563
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2564 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2565 {
2566 ImGuiContext& g = *GImGui;
2567 SetNavID(id, nav_layer);
2568 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2569 g.NavMousePosDirty = true;
2570 g.NavDisableHighlight = false;
2571 g.NavDisableMouseHover = true;
2572 }
2573
SetActiveID(ImGuiID id,ImGuiWindow * window)2574 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2575 {
2576 ImGuiContext& g = *GImGui;
2577 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2578 if (g.ActiveIdIsJustActivated)
2579 {
2580 g.ActiveIdTimer = 0.0f;
2581 g.ActiveIdHasBeenEdited = false;
2582 if (id != 0)
2583 {
2584 g.LastActiveId = id;
2585 g.LastActiveIdTimer = 0.0f;
2586 }
2587 }
2588 g.ActiveId = id;
2589 g.ActiveIdAllowNavDirFlags = 0;
2590 g.ActiveIdAllowOverlap = false;
2591 g.ActiveIdWindow = window;
2592 if (id)
2593 {
2594 g.ActiveIdIsAlive = id;
2595 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2596 }
2597 }
2598
2599 // FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring.
SetFocusID(ImGuiID id,ImGuiWindow * window)2600 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2601 {
2602 ImGuiContext& g = *GImGui;
2603 IM_ASSERT(id != 0);
2604
2605 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2606 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
2607 if (g.NavWindow != window)
2608 g.NavInitRequest = false;
2609 g.NavId = id;
2610 g.NavWindow = window;
2611 g.NavLayer = nav_layer;
2612 window->NavLastIds[nav_layer] = id;
2613 if (window->DC.LastItemId == id)
2614 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2615
2616 if (g.ActiveIdSource == ImGuiInputSource_Nav)
2617 g.NavDisableMouseHover = true;
2618 else
2619 g.NavDisableHighlight = true;
2620 }
2621
ClearActiveID()2622 void ImGui::ClearActiveID()
2623 {
2624 SetActiveID(0, NULL);
2625 }
2626
SetHoveredID(ImGuiID id)2627 void ImGui::SetHoveredID(ImGuiID id)
2628 {
2629 ImGuiContext& g = *GImGui;
2630 g.HoveredId = id;
2631 g.HoveredIdAllowOverlap = false;
2632 if (id != 0 && g.HoveredIdPreviousFrame != id)
2633 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
2634 }
2635
GetHoveredID()2636 ImGuiID ImGui::GetHoveredID()
2637 {
2638 ImGuiContext& g = *GImGui;
2639 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2640 }
2641
KeepAliveID(ImGuiID id)2642 void ImGui::KeepAliveID(ImGuiID id)
2643 {
2644 ImGuiContext& g = *GImGui;
2645 if (g.ActiveId == id)
2646 g.ActiveIdIsAlive = id;
2647 if (g.ActiveIdPreviousFrame == id)
2648 g.ActiveIdPreviousFrameIsAlive = true;
2649 }
2650
MarkItemEdited(ImGuiID id)2651 void ImGui::MarkItemEdited(ImGuiID id)
2652 {
2653 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2654 // 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.
2655 (void)id; // Avoid unused variable warnings when asserts are compiled out.
2656 ImGuiContext& g = *GImGui;
2657 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2658 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2659 g.ActiveIdHasBeenEdited = true;
2660 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2661 }
2662
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2663 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2664 {
2665 // An active popup disable hovering on other windows (apart from its own children)
2666 // FIXME-OPT: This could be cached/stored within the window.
2667 ImGuiContext& g = *GImGui;
2668 if (g.NavWindow)
2669 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2670 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2671 {
2672 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2673 // NB: The order of those two tests is important because Modal windows are also Popups.
2674 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2675 return false;
2676 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2677 return false;
2678 }
2679
2680 return true;
2681 }
2682
2683 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2684 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2685 {
2686 ImGuiContext& g = *GImGui;
2687 ImGuiWindow* window = g.CurrentWindow;
2688 if (window->SkipItems)
2689 return;
2690
2691 // Always align ourselves on pixel boundaries
2692 const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2693 const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2694 //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]
2695 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2696 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
2697 window->DC.CursorPos.y = (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y);
2698 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2699 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2700 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2701
2702 window->DC.PrevLineSize.y = line_height;
2703 window->DC.PrevLineTextBaseOffset = text_base_offset;
2704 window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2705
2706 // Horizontal layout mode
2707 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2708 SameLine();
2709 }
2710
ItemSize(const ImRect & bb,float text_offset_y)2711 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2712 {
2713 ItemSize(bb.GetSize(), text_offset_y);
2714 }
2715
2716 // Declare item bounding box for clipping and interaction.
2717 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2718 // 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)2719 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2720 {
2721 ImGuiContext& g = *GImGui;
2722 ImGuiWindow* window = g.CurrentWindow;
2723
2724 if (id != 0)
2725 {
2726 // Navigation processing runs prior to clipping early-out
2727 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2728 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window.
2729 // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
2730 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick)
2731 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2732 if (g.NavId == id || g.NavAnyRequest)
2733 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2734 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2735 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2736 }
2737
2738 window->DC.LastItemId = id;
2739 window->DC.LastItemRect = bb;
2740 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None;
2741
2742 #ifdef IMGUI_ENABLE_TEST_ENGINE
2743 if (id != 0)
2744 ImGuiTestEngineHook_ItemAdd(&g, bb, id);
2745 #endif
2746
2747 // Clipping test
2748 const bool is_clipped = IsClippedEx(bb, id, false);
2749 if (is_clipped)
2750 return false;
2751 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2752
2753 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2754 if (IsMouseHoveringRect(bb.Min, bb.Max))
2755 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2756 return true;
2757 }
2758
2759 // This is roughly matching the behavior of internal-facing ItemHoverable()
2760 // - 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()
2761 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2762 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2763 {
2764 ImGuiContext& g = *GImGui;
2765 ImGuiWindow* window = g.CurrentWindow;
2766 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2767 return IsItemFocused();
2768
2769 // Test for bounding box overlap, as updated as ItemAdd()
2770 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2771 return false;
2772 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2773
2774 // Test if we are hovering the right window (our window could be behind another window)
2775 // [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.
2776 // 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.
2777 //if (g.HoveredWindow != window)
2778 // return false;
2779 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2780 return false;
2781
2782 // Test if another item is active (e.g. being dragged)
2783 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2784 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2785 return false;
2786
2787 // Test if interactions on this window are blocked by an active popup or modal
2788 if (!IsWindowContentHoverable(window, flags))
2789 return false;
2790
2791 // Test if the item is disabled
2792 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2793 return false;
2794
2795 // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
2796 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2797 return false;
2798 return true;
2799 }
2800
2801 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2802 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2803 {
2804 ImGuiContext& g = *GImGui;
2805 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2806 return false;
2807
2808 ImGuiWindow* window = g.CurrentWindow;
2809 if (g.HoveredWindow != window)
2810 return false;
2811 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2812 return false;
2813 if (!IsMouseHoveringRect(bb.Min, bb.Max))
2814 return false;
2815 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2816 return false;
2817 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2818 return false;
2819
2820 SetHoveredID(id);
2821 return true;
2822 }
2823
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2824 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2825 {
2826 ImGuiContext& g = *GImGui;
2827 ImGuiWindow* window = g.CurrentWindow;
2828 if (!bb.Overlaps(window->ClipRect))
2829 if (id == 0 || id != g.ActiveId)
2830 if (clip_even_when_logged || !g.LogEnabled)
2831 return true;
2832 return false;
2833 }
2834
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2835 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2836 {
2837 ImGuiContext& g = *GImGui;
2838
2839 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0;
2840 window->FocusIdxAllCounter++;
2841 if (is_tab_stop)
2842 window->FocusIdxTabCounter++;
2843
2844 // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2845 // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2846 if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2847 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (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.
2848
2849 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2850 return true;
2851 if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2852 {
2853 g.NavJustTabbedId = id;
2854 return true;
2855 }
2856
2857 return false;
2858 }
2859
FocusableItemUnregister(ImGuiWindow * window)2860 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2861 {
2862 window->FocusIdxAllCounter--;
2863 window->FocusIdxTabCounter--;
2864 }
2865
CalcItemSize(ImVec2 size,float default_x,float default_y)2866 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2867 {
2868 ImGuiContext& g = *GImGui;
2869 ImVec2 content_max;
2870 if (size.x < 0.0f || size.y < 0.0f)
2871 content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2872 if (size.x <= 0.0f)
2873 size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2874 if (size.y <= 0.0f)
2875 size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2876 return size;
2877 }
2878
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2879 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2880 {
2881 if (wrap_pos_x < 0.0f)
2882 return 0.0f;
2883
2884 ImGuiWindow* window = GetCurrentWindowRead();
2885 if (wrap_pos_x == 0.0f)
2886 wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2887 else if (wrap_pos_x > 0.0f)
2888 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2889
2890 return ImMax(wrap_pos_x - pos.x, 1.0f);
2891 }
2892
MemAlloc(size_t size)2893 void* ImGui::MemAlloc(size_t size)
2894 {
2895 if (ImGuiContext* ctx = GImGui)
2896 ctx->IO.MetricsActiveAllocations++;
2897 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2898 }
2899
MemFree(void * ptr)2900 void ImGui::MemFree(void* ptr)
2901 {
2902 if (ptr)
2903 if (ImGuiContext* ctx = GImGui)
2904 ctx->IO.MetricsActiveAllocations--;
2905 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2906 }
2907
GetClipboardText()2908 const char* ImGui::GetClipboardText()
2909 {
2910 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2911 }
2912
SetClipboardText(const char * text)2913 void ImGui::SetClipboardText(const char* text)
2914 {
2915 if (GImGui->IO.SetClipboardTextFn)
2916 GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2917 }
2918
GetVersion()2919 const char* ImGui::GetVersion()
2920 {
2921 return IMGUI_VERSION;
2922 }
2923
2924 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2925 // 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()2926 ImGuiContext* ImGui::GetCurrentContext()
2927 {
2928 return GImGui;
2929 }
2930
SetCurrentContext(ImGuiContext * ctx)2931 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2932 {
2933 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2934 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2935 #else
2936 GImGui = ctx;
2937 #endif
2938 }
2939
2940 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2941 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic.
DebugCheckVersionAndDataLayout(const char * version,size_t sz_io,size_t sz_style,size_t sz_vec2,size_t sz_vec4,size_t sz_vert)2942 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)
2943 {
2944 bool error = false;
2945 if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
2946 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
2947 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
2948 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
2949 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
2950 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
2951 return !error;
2952 }
2953
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2954 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2955 {
2956 GImAllocatorAllocFunc = alloc_func;
2957 GImAllocatorFreeFunc = free_func;
2958 GImAllocatorUserData = user_data;
2959 }
2960
CreateContext(ImFontAtlas * shared_font_atlas)2961 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2962 {
2963 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2964 if (GImGui == NULL)
2965 SetCurrentContext(ctx);
2966 Initialize(ctx);
2967 return ctx;
2968 }
2969
DestroyContext(ImGuiContext * ctx)2970 void ImGui::DestroyContext(ImGuiContext* ctx)
2971 {
2972 if (ctx == NULL)
2973 ctx = GImGui;
2974 Shutdown(ctx);
2975 if (GImGui == ctx)
2976 SetCurrentContext(NULL);
2977 IM_DELETE(ctx);
2978 }
2979
GetIO()2980 ImGuiIO& ImGui::GetIO()
2981 {
2982 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2983 return GImGui->IO;
2984 }
2985
GetStyle()2986 ImGuiStyle& ImGui::GetStyle()
2987 {
2988 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2989 return GImGui->Style;
2990 }
2991
2992 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2993 ImDrawData* ImGui::GetDrawData()
2994 {
2995 ImGuiContext& g = *GImGui;
2996 return g.DrawData.Valid ? &g.DrawData : NULL;
2997 }
2998
GetTime()2999 double ImGui::GetTime()
3000 {
3001 return GImGui->Time;
3002 }
3003
GetFrameCount()3004 int ImGui::GetFrameCount()
3005 {
3006 return GImGui->FrameCount;
3007 }
3008
GetOverlayDrawList(ImGuiWindow *)3009 static ImDrawList* GetOverlayDrawList(ImGuiWindow*)
3010 {
3011 // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches.
3012 return &GImGui->OverlayDrawList;
3013 }
3014
GetOverlayDrawList()3015 ImDrawList* ImGui::GetOverlayDrawList()
3016 {
3017 return &GImGui->OverlayDrawList;
3018 }
3019
GetDrawListSharedData()3020 ImDrawListSharedData* ImGui::GetDrawListSharedData()
3021 {
3022 return &GImGui->DrawListSharedData;
3023 }
3024
StartMouseMovingWindow(ImGuiWindow * window)3025 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
3026 {
3027 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
3028 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
3029 // This is because we want ActiveId to be set even when the window is stuck from moving.
3030 ImGuiContext& g = *GImGui;
3031 FocusWindow(window);
3032 SetActiveID(window->MoveId, window);
3033 g.NavDisableHighlight = true;
3034 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
3035 if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
3036 g.MovingWindow = window;
3037 }
3038
3039 // Handle mouse moving window
3040 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindowNewFrame()3041 void ImGui::UpdateMouseMovingWindowNewFrame()
3042 {
3043 ImGuiContext& g = *GImGui;
3044 if (g.MovingWindow != NULL)
3045 {
3046 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
3047 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
3048 KeepAliveID(g.ActiveId);
3049 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
3050 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
3051 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
3052 {
3053 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
3054 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
3055 {
3056 MarkIniSettingsDirty(moving_window);
3057 SetWindowPos(moving_window, pos, ImGuiCond_Always);
3058 }
3059 FocusWindow(g.MovingWindow);
3060 }
3061 else
3062 {
3063 ClearActiveID();
3064 g.MovingWindow = NULL;
3065 }
3066 }
3067 else
3068 {
3069 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
3070 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
3071 {
3072 KeepAliveID(g.ActiveId);
3073 if (!g.IO.MouseDown[0])
3074 ClearActiveID();
3075 }
3076 }
3077 }
3078
3079 // Initiate moving window, handle left-click and right-click focus
UpdateMouseMovingWindowEndFrame()3080 void ImGui::UpdateMouseMovingWindowEndFrame()
3081 {
3082 // Initiate moving window
3083 ImGuiContext& g = *GImGui;
3084 if (g.ActiveId != 0 || g.HoveredId != 0)
3085 return;
3086
3087 // Unless we just made a window/popup appear
3088 if (g.NavWindow && g.NavWindow->Appearing)
3089 return;
3090
3091 // Click to focus window and start moving (after we're done with all our widgets)
3092 if (g.IO.MouseClicked[0])
3093 {
3094 if (g.HoveredRootWindow != NULL)
3095 {
3096 StartMouseMovingWindow(g.HoveredWindow);
3097 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar))
3098 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
3099 g.MovingWindow = NULL;
3100 }
3101 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3102 {
3103 FocusWindow(NULL); // Clicking on void disable focus
3104 }
3105 }
3106
3107 // With right mouse button we close popups without changing focus
3108 // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3109 if (g.IO.MouseClicked[1])
3110 {
3111 // Find the top-most window between HoveredWindow and the front most Modal Window.
3112 // This is where we can trim the popup stack.
3113 ImGuiWindow* modal = GetFrontMostPopupModal();
3114 bool hovered_window_above_modal = false;
3115 if (modal == NULL)
3116 hovered_window_above_modal = true;
3117 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3118 {
3119 ImGuiWindow* window = g.Windows[i];
3120 if (window == modal)
3121 break;
3122 if (window == g.HoveredWindow)
3123 hovered_window_above_modal = true;
3124 }
3125 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3126 }
3127 }
3128
IsWindowActiveAndVisible(ImGuiWindow * window)3129 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
3130 {
3131 return (window->Active) && (!window->Hidden);
3132 }
3133
UpdateMouseInputs()3134 static void ImGui::UpdateMouseInputs()
3135 {
3136 ImGuiContext& g = *GImGui;
3137
3138 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
3139 if (IsMousePosValid(&g.IO.MousePos))
3140 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos);
3141
3142 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
3143 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
3144 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
3145 else
3146 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
3147 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
3148 g.NavDisableMouseHover = false;
3149
3150 g.IO.MousePosPrev = g.IO.MousePos;
3151 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3152 {
3153 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
3154 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
3155 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
3156 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;
3157 g.IO.MouseDoubleClicked[i] = false;
3158 if (g.IO.MouseClicked[i])
3159 {
3160 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
3161 {
3162 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3163 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
3164 g.IO.MouseDoubleClicked[i] = true;
3165 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
3166 }
3167 else
3168 {
3169 g.IO.MouseClickedTime[i] = g.Time;
3170 }
3171 g.IO.MouseClickedPos[i] = g.IO.MousePos;
3172 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
3173 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
3174 }
3175 else if (g.IO.MouseDown[i])
3176 {
3177 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
3178 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
3179 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
3180 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);
3181 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);
3182 }
3183 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
3184 g.NavDisableMouseHover = false;
3185 }
3186 }
3187
UpdateMouseWheel()3188 void ImGui::UpdateMouseWheel()
3189 {
3190 ImGuiContext& g = *GImGui;
3191 if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
3192 return;
3193 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
3194 return;
3195
3196 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set).
3197 ImGuiWindow* window = g.HoveredWindow;
3198 ImGuiWindow* scroll_window = window;
3199 while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow)
3200 scroll_window = scroll_window->ParentWindow;
3201 const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs);
3202
3203 if (g.IO.MouseWheel != 0.0f)
3204 {
3205 if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
3206 {
3207 // Zoom / Scale window
3208 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
3209 const float scale = new_font_scale / window->FontWindowScale;
3210 window->FontWindowScale = new_font_scale;
3211
3212 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3213 window->Pos += offset;
3214 window->Size *= scale;
3215 window->SizeFull *= scale;
3216 }
3217 else if (!g.IO.KeyCtrl && scroll_allowed)
3218 {
3219 // Mouse wheel vertical scrolling
3220 float scroll_amount = 5 * scroll_window->CalcFontSize();
3221 scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3222 SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3223 }
3224 }
3225 if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3226 {
3227 // Mouse wheel horizontal scrolling (for hardware that supports it)
3228 float scroll_amount = scroll_window->CalcFontSize();
3229 SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3230 }
3231 }
3232
3233 // 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()3234 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3235 {
3236 ImGuiContext& g = *GImGui;
3237
3238 // Find the window hovered by mouse:
3239 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3240 // - 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.
3241 // - 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.
3242 FindHoveredWindow();
3243
3244 // Modal windows prevents cursor from hovering behind them.
3245 ImGuiWindow* modal_window = GetFrontMostPopupModal();
3246 if (modal_window)
3247 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3248 g.HoveredRootWindow = g.HoveredWindow = NULL;
3249
3250 // Disabled mouse?
3251 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3252 g.HoveredWindow = g.HoveredRootWindow = NULL;
3253
3254 // 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.
3255 int mouse_earliest_button_down = -1;
3256 bool mouse_any_down = false;
3257 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3258 {
3259 if (g.IO.MouseClicked[i])
3260 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3261 mouse_any_down |= g.IO.MouseDown[i];
3262 if (g.IO.MouseDown[i])
3263 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3264 mouse_earliest_button_down = i;
3265 }
3266 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3267
3268 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3269 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3270 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3271 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3272 g.HoveredWindow = g.HoveredRootWindow = NULL;
3273
3274 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3275 if (g.WantCaptureMouseNextFrame != -1)
3276 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3277 else
3278 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3279
3280 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3281 if (g.WantCaptureKeyboardNextFrame != -1)
3282 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3283 else
3284 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3285 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3286 g.IO.WantCaptureKeyboard = true;
3287
3288 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3289 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3290 }
3291
NewFrame()3292 void ImGui::NewFrame()
3293 {
3294 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3295 ImGuiContext& g = *GImGui;
3296
3297 #ifdef IMGUI_ENABLE_TEST_ENGINE
3298 ImGuiTestEngineHook_PreNewFrame(&g);
3299 #endif
3300
3301 // Check user data
3302 // (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)
3303 IM_ASSERT(g.Initialized);
3304 IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3305 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value");
3306 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3307 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3308 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting");
3309 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)");
3310 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3311 for (int n = 0; n < ImGuiKey_COUNT; n++)
3312 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)");
3313
3314 // 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)
3315 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3316 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3317
3318 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3319 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3320 g.IO.ConfigWindowsResizeFromEdges = false;
3321
3322 // Load settings on first frame (if not explicitly loaded manually before)
3323 if (!g.SettingsLoaded)
3324 {
3325 IM_ASSERT(g.SettingsWindows.empty());
3326 if (g.IO.IniFilename)
3327 LoadIniSettingsFromDisk(g.IO.IniFilename);
3328 g.SettingsLoaded = true;
3329 }
3330
3331 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3332 if (g.SettingsDirtyTimer > 0.0f)
3333 {
3334 g.SettingsDirtyTimer -= g.IO.DeltaTime;
3335 if (g.SettingsDirtyTimer <= 0.0f)
3336 {
3337 if (g.IO.IniFilename != NULL)
3338 SaveIniSettingsToDisk(g.IO.IniFilename);
3339 else
3340 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3341 g.SettingsDirtyTimer = 0.0f;
3342 }
3343 }
3344
3345 g.Time += g.IO.DeltaTime;
3346 g.FrameScopeActive = true;
3347 g.FrameCount += 1;
3348 g.TooltipOverrideCount = 0;
3349 g.WindowsActiveCount = 0;
3350
3351 // Setup current font and draw list
3352 g.IO.Fonts->Locked = true;
3353 SetCurrentFont(GetDefaultFont());
3354 IM_ASSERT(g.Font->IsLoaded());
3355 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3356 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3357
3358 g.OverlayDrawList.Clear();
3359 g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3360 g.OverlayDrawList.PushClipRectFullScreen();
3361 g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3362
3363 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3364 g.DrawData.Clear();
3365
3366 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3367 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3368 KeepAliveID(g.DragDropPayload.SourceId);
3369
3370 // Clear reference to active widget if the widget isn't alive anymore
3371 if (!g.HoveredIdPreviousFrame)
3372 g.HoveredIdTimer = 0.0f;
3373 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
3374 g.HoveredIdNotActiveTimer = 0.0f;
3375 if (g.HoveredId)
3376 g.HoveredIdTimer += g.IO.DeltaTime;
3377 if (g.HoveredId && g.ActiveId != g.HoveredId)
3378 g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
3379 g.HoveredIdPreviousFrame = g.HoveredId;
3380 g.HoveredId = 0;
3381 g.HoveredIdAllowOverlap = false;
3382 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3383 ClearActiveID();
3384 if (g.ActiveId)
3385 g.ActiveIdTimer += g.IO.DeltaTime;
3386 g.LastActiveIdTimer += g.IO.DeltaTime;
3387 g.ActiveIdPreviousFrame = g.ActiveId;
3388 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3389 g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3390 g.ActiveIdIsAlive = 0;
3391 g.ActiveIdPreviousFrameIsAlive = false;
3392 g.ActiveIdIsJustActivated = false;
3393 if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3394 g.ScalarAsInputTextId = 0;
3395
3396 // Drag and drop
3397 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3398 g.DragDropAcceptIdCurr = 0;
3399 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3400 g.DragDropWithinSourceOrTarget = false;
3401
3402 // Update keyboard input state
3403 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3404 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3405 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;
3406
3407 // Update gamepad/keyboard directional navigation
3408 NavUpdate();
3409
3410 // Update mouse input state
3411 UpdateMouseInputs();
3412
3413 // Calculate frame-rate for the user, as a purely luxurious feature
3414 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3415 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3416 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3417 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3418
3419 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3420 UpdateMouseMovingWindowNewFrame();
3421 UpdateHoveredWindowAndCaptureFlags();
3422
3423 // Background darkening/whitening
3424 if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3425 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3426 else
3427 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3428
3429 g.MouseCursor = ImGuiMouseCursor_Arrow;
3430 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3431 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3432
3433 // Mouse wheel scrolling, scale
3434 UpdateMouseWheel();
3435
3436 // Pressing TAB activate widget focus
3437 if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3438 {
3439 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3440 g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3441 else
3442 g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3443 }
3444 g.NavIdTabCounter = INT_MAX;
3445
3446 // Mark all windows as not visible
3447 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size);
3448 for (int i = 0; i != g.Windows.Size; i++)
3449 {
3450 ImGuiWindow* window = g.Windows[i];
3451 window->WasActive = window->Active;
3452 window->Active = false;
3453 window->WriteAccessed = false;
3454 }
3455
3456 // Closing the focused window restore focus to the first active root window in descending z-order
3457 if (g.NavWindow && !g.NavWindow->WasActive)
3458 FocusPreviousWindowIgnoringOne(NULL);
3459
3460 // No window should be open at the beginning of the frame.
3461 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3462 g.CurrentWindowStack.resize(0);
3463 g.BeginPopupStack.resize(0);
3464 ClosePopupsOverWindow(g.NavWindow);
3465
3466 // Create implicit/fallback window - which we will only render it if the user has added something to it.
3467 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3468 // This fallback is particularly important as it avoid ImGui:: calls from crashing.
3469 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3470 Begin("Debug##Default");
3471 g.FrameScopePushedImplicitWindow = true;
3472
3473 #ifdef IMGUI_ENABLE_TEST_ENGINE
3474 ImGuiTestEngineHook_PostNewFrame(&g);
3475 #endif
3476 }
3477
Initialize(ImGuiContext * context)3478 void ImGui::Initialize(ImGuiContext* context)
3479 {
3480 ImGuiContext& g = *context;
3481 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3482
3483 // Add .ini handle for ImGuiWindow type
3484 ImGuiSettingsHandler ini_handler;
3485 ini_handler.TypeName = "Window";
3486 ini_handler.TypeHash = ImHash("Window", 0, 0);
3487 ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3488 ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3489 ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3490 g.SettingsHandlers.push_front(ini_handler);
3491
3492 g.Initialized = true;
3493 }
3494
3495 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3496 void ImGui::Shutdown(ImGuiContext* context)
3497 {
3498 // 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)
3499 ImGuiContext& g = *context;
3500 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3501 {
3502 g.IO.Fonts->Locked = false;
3503 IM_DELETE(g.IO.Fonts);
3504 }
3505 g.IO.Fonts = NULL;
3506
3507 // Cleanup of other data are conditional on actually having initialized ImGui.
3508 if (!g.Initialized)
3509 return;
3510
3511 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3512 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3513 {
3514 ImGuiContext* backup_context = GImGui;
3515 SetCurrentContext(context);
3516 SaveIniSettingsToDisk(g.IO.IniFilename);
3517 SetCurrentContext(backup_context);
3518 }
3519
3520 // Clear everything else
3521 for (int i = 0; i < g.Windows.Size; i++)
3522 IM_DELETE(g.Windows[i]);
3523 g.Windows.clear();
3524 g.WindowsFocusOrder.clear();
3525 g.WindowsSortBuffer.clear();
3526 g.CurrentWindow = NULL;
3527 g.CurrentWindowStack.clear();
3528 g.WindowsById.Clear();
3529 g.NavWindow = NULL;
3530 g.HoveredWindow = NULL;
3531 g.HoveredRootWindow = NULL;
3532 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3533 g.MovingWindow = NULL;
3534 g.ColorModifiers.clear();
3535 g.StyleModifiers.clear();
3536 g.FontStack.clear();
3537 g.OpenPopupStack.clear();
3538 g.BeginPopupStack.clear();
3539 g.DrawDataBuilder.ClearFreeMemory();
3540 g.OverlayDrawList.ClearFreeMemory();
3541 g.PrivateClipboard.clear();
3542 g.InputTextState.TextW.clear();
3543 g.InputTextState.InitialText.clear();
3544 g.InputTextState.TempBuffer.clear();
3545
3546 for (int i = 0; i < g.SettingsWindows.Size; i++)
3547 IM_DELETE(g.SettingsWindows[i].Name);
3548 g.SettingsWindows.clear();
3549 g.SettingsHandlers.clear();
3550
3551 if (g.LogFile && g.LogFile != stdout)
3552 {
3553 fclose(g.LogFile);
3554 g.LogFile = NULL;
3555 }
3556 g.LogClipboard.clear();
3557
3558 g.Initialized = false;
3559 }
3560
3561 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3562 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3563 {
3564 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3565 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3566 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3567 return d;
3568 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3569 return d;
3570 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3571 }
3572
AddWindowToSortBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3573 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3574 {
3575 out_sorted_windows->push_back(window);
3576 if (window->Active)
3577 {
3578 int count = window->DC.ChildWindows.Size;
3579 if (count > 1)
3580 ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3581 for (int i = 0; i < count; i++)
3582 {
3583 ImGuiWindow* child = window->DC.ChildWindows[i];
3584 if (child->Active)
3585 AddWindowToSortBuffer(out_sorted_windows, child);
3586 }
3587 }
3588 }
3589
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3590 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3591 {
3592 if (draw_list->CmdBuffer.empty())
3593 return;
3594
3595 // Remove trailing command if unused
3596 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3597 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3598 {
3599 draw_list->CmdBuffer.pop_back();
3600 if (draw_list->CmdBuffer.empty())
3601 return;
3602 }
3603
3604 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly.
3605 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3606 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3607 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3608
3609 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3610 // If this assert triggers because you are drawing lots of stuff manually:
3611 // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents.
3612 // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes.
3613 // You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing:
3614 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3615 // Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API.
3616 // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists.
3617 if (sizeof(ImDrawIdx) == 2)
3618 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3619
3620 out_list->push_back(draw_list);
3621 }
3622
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3623 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3624 {
3625 ImGuiContext& g = *GImGui;
3626 g.IO.MetricsRenderWindows++;
3627 AddDrawListToDrawData(out_render_list, window->DrawList);
3628 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3629 {
3630 ImGuiWindow* child = window->DC.ChildWindows[i];
3631 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3632 AddWindowToDrawData(out_render_list, child);
3633 }
3634 }
3635
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3636 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3637 {
3638 ImGuiContext& g = *GImGui;
3639 if (window->Flags & ImGuiWindowFlags_Tooltip)
3640 AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3641 else
3642 AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3643 }
3644
FlattenIntoSingleLayer()3645 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3646 {
3647 int n = Layers[0].Size;
3648 int size = n;
3649 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3650 size += Layers[i].Size;
3651 Layers[0].resize(size);
3652 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3653 {
3654 ImVector<ImDrawList*>& layer = Layers[layer_n];
3655 if (layer.empty())
3656 continue;
3657 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3658 n += layer.Size;
3659 layer.resize(0);
3660 }
3661 }
3662
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3663 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3664 {
3665 ImGuiIO& io = ImGui::GetIO();
3666 draw_data->Valid = true;
3667 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3668 draw_data->CmdListsCount = draw_lists->Size;
3669 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3670 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3671 draw_data->DisplaySize = io.DisplaySize;
3672 for (int n = 0; n < draw_lists->Size; n++)
3673 {
3674 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3675 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3676 }
3677 }
3678
3679 // 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)3680 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3681 {
3682 ImGuiWindow* window = GetCurrentWindow();
3683 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3684 window->ClipRect = window->DrawList->_ClipRectStack.back();
3685 }
3686
PopClipRect()3687 void ImGui::PopClipRect()
3688 {
3689 ImGuiWindow* window = GetCurrentWindow();
3690 window->DrawList->PopClipRect();
3691 window->ClipRect = window->DrawList->_ClipRectStack.back();
3692 }
3693
3694 // 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()3695 void ImGui::EndFrame()
3696 {
3697 ImGuiContext& g = *GImGui;
3698 IM_ASSERT(g.Initialized);
3699 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
3700 return;
3701 IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?");
3702
3703 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3704 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
3705 {
3706 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3707 g.PlatformImeLastPos = g.PlatformImePos;
3708 }
3709
3710 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
3711 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
3712 if (g.CurrentWindowStack.Size != 1)
3713 {
3714 if (g.CurrentWindowStack.Size > 1)
3715 {
3716 IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?");
3717 while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING
3718 End();
3719 }
3720 else
3721 {
3722 IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?");
3723 }
3724 }
3725
3726 // Hide implicit/fallback "Debug" window if it hasn't been used
3727 g.FrameScopePushedImplicitWindow = false;
3728 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3729 g.CurrentWindow->Active = false;
3730 End();
3731
3732 // Show CTRL+TAB list window
3733 if (g.NavWindowingTarget)
3734 NavUpdateWindowingList();
3735
3736 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3737 if (g.DragDropActive)
3738 {
3739 bool is_delivered = g.DragDropPayload.Delivery;
3740 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3741 if (is_delivered || is_elapsed)
3742 ClearDragDrop();
3743 }
3744
3745 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3746 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3747 {
3748 g.DragDropWithinSourceOrTarget = true;
3749 SetTooltip("...");
3750 g.DragDropWithinSourceOrTarget = false;
3751 }
3752
3753 // End frame
3754 g.FrameScopeActive = false;
3755 g.FrameCountEnded = g.FrameCount;
3756
3757 // Initiate moving window + handle left-click and right-click focus
3758 UpdateMouseMovingWindowEndFrame();
3759
3760 // Sort the window list so that all child windows are after their parent
3761 // We cannot do that on FocusWindow() because childs may not exist yet
3762 g.WindowsSortBuffer.resize(0);
3763 g.WindowsSortBuffer.reserve(g.Windows.Size);
3764 for (int i = 0; i != g.Windows.Size; i++)
3765 {
3766 ImGuiWindow* window = g.Windows[i];
3767 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
3768 continue;
3769 AddWindowToSortBuffer(&g.WindowsSortBuffer, window);
3770 }
3771
3772 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
3773 g.Windows.swap(g.WindowsSortBuffer);
3774 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3775
3776 // Unlock font atlas
3777 g.IO.Fonts->Locked = false;
3778
3779 // Clear Input data for next frame
3780 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3781 g.IO.InputQueueCharacters.resize(0);
3782 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3783 }
3784
Render()3785 void ImGui::Render()
3786 {
3787 ImGuiContext& g = *GImGui;
3788 IM_ASSERT(g.Initialized);
3789
3790 if (g.FrameCountEnded != g.FrameCount)
3791 EndFrame();
3792 g.FrameCountRendered = g.FrameCount;
3793
3794 // Gather ImDrawList to render (for each active window)
3795 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3796 g.DrawDataBuilder.Clear();
3797 ImGuiWindow* windows_to_render_front_most[2];
3798 windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3799 windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3800 for (int n = 0; n != g.Windows.Size; n++)
3801 {
3802 ImGuiWindow* window = g.Windows[n];
3803 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3804 AddWindowToDrawDataSelectLayer(window);
3805 }
3806 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3807 if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3808 AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]);
3809 g.DrawDataBuilder.FlattenIntoSingleLayer();
3810
3811 // Draw software mouse cursor if requested
3812 if (g.IO.MouseDrawCursor)
3813 RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3814
3815 if (!g.OverlayDrawList.VtxBuffer.empty())
3816 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3817
3818 // Setup ImDrawData structure for end-user
3819 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3820 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3821 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3822
3823 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves.
3824 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3825 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3826 g.IO.RenderDrawListsFn(&g.DrawData);
3827 #endif
3828 }
3829
3830 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3831 // CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize)
CalcTextSize(const char * text,const char * text_end,bool hide_text_after_double_hash,float wrap_width)3832 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3833 {
3834 ImGuiContext& g = *GImGui;
3835
3836 const char* text_display_end;
3837 if (hide_text_after_double_hash)
3838 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
3839 else
3840 text_display_end = text_end;
3841
3842 ImFont* font = g.Font;
3843 const float font_size = g.FontSize;
3844 if (text == text_display_end)
3845 return ImVec2(0.0f, font_size);
3846 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3847
3848 // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3849 const float font_scale = font_size / font->FontSize;
3850 const float character_spacing_x = 1.0f * font_scale;
3851 if (text_size.x > 0.0f)
3852 text_size.x -= character_spacing_x;
3853 text_size.x = (float)(int)(text_size.x + 0.95f);
3854
3855 return text_size;
3856 }
3857
3858 // Helper to calculate coarse clipping of large list of evenly sized items.
3859 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3860 // 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)3861 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3862 {
3863 ImGuiContext& g = *GImGui;
3864 ImGuiWindow* window = g.CurrentWindow;
3865 if (g.LogEnabled)
3866 {
3867 // If logging is active, do not perform any clipping
3868 *out_items_display_start = 0;
3869 *out_items_display_end = items_count;
3870 return;
3871 }
3872 if (window->SkipItems)
3873 {
3874 *out_items_display_start = *out_items_display_end = 0;
3875 return;
3876 }
3877
3878 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3879 ImRect unclipped_rect = window->ClipRect;
3880 if (g.NavMoveRequest)
3881 unclipped_rect.Add(g.NavScoringRectScreen);
3882
3883 const ImVec2 pos = window->DC.CursorPos;
3884 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3885 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3886
3887 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3888 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3889 start--;
3890 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3891 end++;
3892
3893 start = ImClamp(start, 0, items_count);
3894 end = ImClamp(end + 1, start, items_count);
3895 *out_items_display_start = start;
3896 *out_items_display_end = end;
3897 }
3898
3899 // Find window given position, search front-to-back
3900 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3901 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3902 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()3903 static void FindHoveredWindow()
3904 {
3905 ImGuiContext& g = *GImGui;
3906
3907 ImGuiWindow* hovered_window = NULL;
3908 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
3909 hovered_window = g.MovingWindow;
3910
3911 ImVec2 padding_regular = g.Style.TouchExtraPadding;
3912 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;
3913 for (int i = g.Windows.Size - 1; i >= 0; i--)
3914 {
3915 ImGuiWindow* window = g.Windows[i];
3916 if (!window->Active || window->Hidden)
3917 continue;
3918 if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
3919 continue;
3920
3921 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3922 ImRect bb(window->OuterRectClipped);
3923 if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize))
3924 bb.Expand(padding_regular);
3925 else
3926 bb.Expand(padding_for_resize_from_edges);
3927 if (!bb.Contains(g.IO.MousePos))
3928 continue;
3929
3930 // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches.
3931 if (hovered_window == NULL)
3932 hovered_window = window;
3933 if (hovered_window)
3934 break;
3935 }
3936
3937 g.HoveredWindow = hovered_window;
3938 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3939
3940 }
3941
3942 // Test if mouse cursor is hovering given rectangle
3943 // NB- Rectangle is clipped by our current clip setting
3944 // 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)3945 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3946 {
3947 ImGuiContext& g = *GImGui;
3948
3949 // Clip
3950 ImRect rect_clipped(r_min, r_max);
3951 if (clip)
3952 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
3953
3954 // Expand for touch input
3955 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3956 if (!rect_for_touch.Contains(g.IO.MousePos))
3957 return false;
3958 return true;
3959 }
3960
GetKeyIndex(ImGuiKey imgui_key)3961 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3962 {
3963 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3964 return GImGui->IO.KeyMap[imgui_key];
3965 }
3966
3967 // 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)3968 bool ImGui::IsKeyDown(int user_key_index)
3969 {
3970 if (user_key_index < 0) return false;
3971 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3972 return GImGui->IO.KeysDown[user_key_index];
3973 }
3974
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)3975 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3976 {
3977 if (t == 0.0f)
3978 return 1;
3979 if (t <= repeat_delay || repeat_rate <= 0.0f)
3980 return 0;
3981 const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3982 return (count > 0) ? count : 0;
3983 }
3984
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)3985 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3986 {
3987 ImGuiContext& g = *GImGui;
3988 if (key_index < 0) return false;
3989 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3990 const float t = g.IO.KeysDownDuration[key_index];
3991 return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3992 }
3993
IsKeyPressed(int user_key_index,bool repeat)3994 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3995 {
3996 ImGuiContext& g = *GImGui;
3997 if (user_key_index < 0) return false;
3998 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3999 const float t = g.IO.KeysDownDuration[user_key_index];
4000 if (t == 0.0f)
4001 return true;
4002 if (repeat && t > g.IO.KeyRepeatDelay)
4003 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
4004 return false;
4005 }
4006
IsKeyReleased(int user_key_index)4007 bool ImGui::IsKeyReleased(int user_key_index)
4008 {
4009 ImGuiContext& g = *GImGui;
4010 if (user_key_index < 0) return false;
4011 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
4012 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
4013 }
4014
IsMouseDown(int button)4015 bool ImGui::IsMouseDown(int button)
4016 {
4017 ImGuiContext& g = *GImGui;
4018 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4019 return g.IO.MouseDown[button];
4020 }
4021
IsAnyMouseDown()4022 bool ImGui::IsAnyMouseDown()
4023 {
4024 ImGuiContext& g = *GImGui;
4025 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
4026 if (g.IO.MouseDown[n])
4027 return true;
4028 return false;
4029 }
4030
IsMouseClicked(int button,bool repeat)4031 bool ImGui::IsMouseClicked(int button, bool repeat)
4032 {
4033 ImGuiContext& g = *GImGui;
4034 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4035 const float t = g.IO.MouseDownDuration[button];
4036 if (t == 0.0f)
4037 return true;
4038
4039 if (repeat && t > g.IO.KeyRepeatDelay)
4040 {
4041 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
4042 if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
4043 return true;
4044 }
4045
4046 return false;
4047 }
4048
IsMouseReleased(int button)4049 bool ImGui::IsMouseReleased(int button)
4050 {
4051 ImGuiContext& g = *GImGui;
4052 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4053 return g.IO.MouseReleased[button];
4054 }
4055
IsMouseDoubleClicked(int button)4056 bool ImGui::IsMouseDoubleClicked(int button)
4057 {
4058 ImGuiContext& g = *GImGui;
4059 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4060 return g.IO.MouseDoubleClicked[button];
4061 }
4062
IsMouseDragging(int button,float lock_threshold)4063 bool ImGui::IsMouseDragging(int button, float lock_threshold)
4064 {
4065 ImGuiContext& g = *GImGui;
4066 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4067 if (!g.IO.MouseDown[button])
4068 return false;
4069 if (lock_threshold < 0.0f)
4070 lock_threshold = g.IO.MouseDragThreshold;
4071 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
4072 }
4073
GetMousePos()4074 ImVec2 ImGui::GetMousePos()
4075 {
4076 return GImGui->IO.MousePos;
4077 }
4078
4079 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()4080 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
4081 {
4082 ImGuiContext& g = *GImGui;
4083 if (g.BeginPopupStack.Size > 0)
4084 return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos;
4085 return g.IO.MousePos;
4086 }
4087
4088 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)4089 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
4090 {
4091 if (mouse_pos == NULL)
4092 mouse_pos = &GImGui->IO.MousePos;
4093 const float MOUSE_INVALID = -256000.0f;
4094 return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
4095 }
4096
4097 // Return the delta from the initial clicking position.
4098 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
4099 // 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)4100 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
4101 {
4102 ImGuiContext& g = *GImGui;
4103 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4104 if (lock_threshold < 0.0f)
4105 lock_threshold = g.IO.MouseDragThreshold;
4106 if (g.IO.MouseDown[button])
4107 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
4108 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
4109 return ImVec2(0.0f, 0.0f);
4110 }
4111
ResetMouseDragDelta(int button)4112 void ImGui::ResetMouseDragDelta(int button)
4113 {
4114 ImGuiContext& g = *GImGui;
4115 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
4116 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
4117 g.IO.MouseClickedPos[button] = g.IO.MousePos;
4118 }
4119
GetMouseCursor()4120 ImGuiMouseCursor ImGui::GetMouseCursor()
4121 {
4122 return GImGui->MouseCursor;
4123 }
4124
SetMouseCursor(ImGuiMouseCursor cursor_type)4125 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
4126 {
4127 GImGui->MouseCursor = cursor_type;
4128 }
4129
CaptureKeyboardFromApp(bool capture)4130 void ImGui::CaptureKeyboardFromApp(bool capture)
4131 {
4132 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
4133 }
4134
CaptureMouseFromApp(bool capture)4135 void ImGui::CaptureMouseFromApp(bool capture)
4136 {
4137 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
4138 }
4139
IsItemActive()4140 bool ImGui::IsItemActive()
4141 {
4142 ImGuiContext& g = *GImGui;
4143 if (g.ActiveId)
4144 {
4145 ImGuiWindow* window = g.CurrentWindow;
4146 return g.ActiveId == window->DC.LastItemId;
4147 }
4148 return false;
4149 }
4150
IsItemDeactivated()4151 bool ImGui::IsItemDeactivated()
4152 {
4153 ImGuiContext& g = *GImGui;
4154 ImGuiWindow* window = g.CurrentWindow;
4155 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
4156 }
4157
IsItemDeactivatedAfterEdit()4158 bool ImGui::IsItemDeactivatedAfterEdit()
4159 {
4160 ImGuiContext& g = *GImGui;
4161 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
4162 }
4163
IsItemFocused()4164 bool ImGui::IsItemFocused()
4165 {
4166 ImGuiContext& g = *GImGui;
4167 ImGuiWindow* window = g.CurrentWindow;
4168 return g.NavId && !g.NavDisableHighlight && g.NavId == window->DC.LastItemId;
4169 }
4170
IsItemClicked(int mouse_button)4171 bool ImGui::IsItemClicked(int mouse_button)
4172 {
4173 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
4174 }
4175
IsAnyItemHovered()4176 bool ImGui::IsAnyItemHovered()
4177 {
4178 ImGuiContext& g = *GImGui;
4179 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
4180 }
4181
IsAnyItemActive()4182 bool ImGui::IsAnyItemActive()
4183 {
4184 ImGuiContext& g = *GImGui;
4185 return g.ActiveId != 0;
4186 }
4187
IsAnyItemFocused()4188 bool ImGui::IsAnyItemFocused()
4189 {
4190 ImGuiContext& g = *GImGui;
4191 return g.NavId != 0 && !g.NavDisableHighlight;
4192 }
4193
IsItemVisible()4194 bool ImGui::IsItemVisible()
4195 {
4196 ImGuiWindow* window = GetCurrentWindowRead();
4197 return window->ClipRect.Overlaps(window->DC.LastItemRect);
4198 }
4199
IsItemEdited()4200 bool ImGui::IsItemEdited()
4201 {
4202 ImGuiWindow* window = GetCurrentWindowRead();
4203 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
4204 }
4205
4206 // 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()4207 void ImGui::SetItemAllowOverlap()
4208 {
4209 ImGuiContext& g = *GImGui;
4210 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
4211 g.HoveredIdAllowOverlap = true;
4212 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
4213 g.ActiveIdAllowOverlap = true;
4214 }
4215
GetItemRectMin()4216 ImVec2 ImGui::GetItemRectMin()
4217 {
4218 ImGuiWindow* window = GetCurrentWindowRead();
4219 return window->DC.LastItemRect.Min;
4220 }
4221
GetItemRectMax()4222 ImVec2 ImGui::GetItemRectMax()
4223 {
4224 ImGuiWindow* window = GetCurrentWindowRead();
4225 return window->DC.LastItemRect.Max;
4226 }
4227
GetItemRectSize()4228 ImVec2 ImGui::GetItemRectSize()
4229 {
4230 ImGuiWindow* window = GetCurrentWindowRead();
4231 return window->DC.LastItemRect.GetSize();
4232 }
4233
GetViewportRect()4234 static ImRect GetViewportRect()
4235 {
4236 ImGuiContext& g = *GImGui;
4237 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4238 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4239 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4240 }
4241
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4242 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4243 {
4244 ImGuiContext& g = *GImGui;
4245 ImGuiWindow* parent_window = g.CurrentWindow;
4246
4247 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4248 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4249
4250 // Size
4251 const ImVec2 content_avail = GetContentRegionAvail();
4252 ImVec2 size = ImFloor(size_arg);
4253 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4254 if (size.x <= 0.0f)
4255 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4256 if (size.y <= 0.0f)
4257 size.y = ImMax(content_avail.y + size.y, 4.0f);
4258 SetNextWindowSize(size);
4259
4260 // 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.
4261 char title[256];
4262 if (name)
4263 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id);
4264 else
4265 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4266
4267 const float backup_border_size = g.Style.ChildBorderSize;
4268 if (!border)
4269 g.Style.ChildBorderSize = 0.0f;
4270 bool ret = Begin(title, NULL, flags);
4271 g.Style.ChildBorderSize = backup_border_size;
4272
4273 ImGuiWindow* child_window = g.CurrentWindow;
4274 child_window->ChildId = id;
4275 child_window->AutoFitChildAxises = auto_fit_axises;
4276
4277 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
4278 // While this is not really documented/defined, it seems that the expected thing to do.
4279 if (child_window->BeginCount == 1)
4280 parent_window->DC.CursorPos = child_window->Pos;
4281
4282 // Process navigation-in immediately so NavInit can run on first frame
4283 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4284 {
4285 FocusWindow(child_window);
4286 NavInitWindow(child_window, false);
4287 SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4288 g.ActiveIdSource = ImGuiInputSource_Nav;
4289 }
4290 return ret;
4291 }
4292
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4293 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4294 {
4295 ImGuiWindow* window = GetCurrentWindow();
4296 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4297 }
4298
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4299 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4300 {
4301 IM_ASSERT(id != 0);
4302 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4303 }
4304
EndChild()4305 void ImGui::EndChild()
4306 {
4307 ImGuiContext& g = *GImGui;
4308 ImGuiWindow* window = g.CurrentWindow;
4309
4310 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
4311 if (window->BeginCount > 1)
4312 {
4313 End();
4314 }
4315 else
4316 {
4317 ImVec2 sz = window->Size;
4318 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4319 sz.x = ImMax(4.0f, sz.x);
4320 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4321 sz.y = ImMax(4.0f, sz.y);
4322 End();
4323
4324 ImGuiWindow* parent_window = g.CurrentWindow;
4325 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4326 ItemSize(sz);
4327 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4328 {
4329 ItemAdd(bb, window->ChildId);
4330 RenderNavHighlight(bb, window->ChildId);
4331
4332 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4333 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4334 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4335 }
4336 else
4337 {
4338 // Not navigable into
4339 ItemAdd(bb, 0);
4340 }
4341 }
4342 }
4343
4344 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4345 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4346 {
4347 ImGuiContext& g = *GImGui;
4348 const ImGuiStyle& style = g.Style;
4349 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4350 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4351 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4352 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4353 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4354 PopStyleVar(3);
4355 PopStyleColor();
4356 return ret;
4357 }
4358
EndChildFrame()4359 void ImGui::EndChildFrame()
4360 {
4361 EndChild();
4362 }
4363
4364 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4365 static void CheckStacksSize(ImGuiWindow* window, bool write)
4366 {
4367 // 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)
4368 ImGuiContext& g = *GImGui;
4369 short* p_backup = &window->DC.StackSizesBackup[0];
4370 { int current = window->IDStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
4371 { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
4372 { int current = g.BeginPopupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4373 // 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.
4374 { int current = g.ColorModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
4375 { int current = g.StyleModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
4376 { int current = g.FontStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
4377 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4378 }
4379
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4380 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4381 {
4382 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4383 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4384 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4385 }
4386
FindWindowByID(ImGuiID id)4387 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
4388 {
4389 ImGuiContext& g = *GImGui;
4390 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4391 }
4392
FindWindowByName(const char * name)4393 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4394 {
4395 ImGuiID id = ImHash(name, 0);
4396 return FindWindowByID(id);
4397 }
4398
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4399 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4400 {
4401 ImGuiContext& g = *GImGui;
4402
4403 // Create window the first time
4404 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4405 window->Flags = flags;
4406 g.WindowsById.SetVoidPtr(window->ID, window);
4407
4408 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4409 window->Pos = ImVec2(60, 60);
4410
4411 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4412 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4413 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4414 {
4415 // Retrieve settings from .ini file
4416 window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
4417 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4418 window->Pos = ImFloor(settings->Pos);
4419 window->Collapsed = settings->Collapsed;
4420 if (ImLengthSqr(settings->Size) > 0.00001f)
4421 size = ImFloor(settings->Size);
4422 }
4423 window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(size);
4424 window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4425
4426 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4427 {
4428 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4429 window->AutoFitOnlyGrows = false;
4430 }
4431 else
4432 {
4433 if (window->Size.x <= 0.0f)
4434 window->AutoFitFramesX = 2;
4435 if (window->Size.y <= 0.0f)
4436 window->AutoFitFramesY = 2;
4437 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4438 }
4439
4440 g.WindowsFocusOrder.push_back(window);
4441 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4442 g.Windows.push_front(window); // Quite slow but rare and only once
4443 else
4444 g.Windows.push_back(window);
4445 return window;
4446 }
4447
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4448 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4449 {
4450 ImGuiContext& g = *GImGui;
4451 if (g.NextWindowData.SizeConstraintCond != 0)
4452 {
4453 // Using -1,-1 on either X/Y axis to preserve the current size.
4454 ImRect cr = g.NextWindowData.SizeConstraintRect;
4455 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4456 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4457 if (g.NextWindowData.SizeCallback)
4458 {
4459 ImGuiSizeCallbackData data;
4460 data.UserData = g.NextWindowData.SizeCallbackUserData;
4461 data.Pos = window->Pos;
4462 data.CurrentSize = window->SizeFull;
4463 data.DesiredSize = new_size;
4464 g.NextWindowData.SizeCallback(&data);
4465 new_size = data.DesiredSize;
4466 }
4467 }
4468
4469 // Minimum size
4470 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4471 {
4472 new_size = ImMax(new_size, g.Style.WindowMinSize);
4473 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
4474 }
4475 return new_size;
4476 }
4477
CalcSizeContents(ImGuiWindow * window)4478 static ImVec2 CalcSizeContents(ImGuiWindow* window)
4479 {
4480 if (window->Collapsed)
4481 return window->SizeContents;
4482 if (window->Hidden && window->HiddenFramesForResize == 0 && window->HiddenFramesRegular > 0)
4483 return window->SizeContents;
4484
4485 ImVec2 sz;
4486 sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4487 sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4488 return sz + window->WindowPadding;
4489 }
4490
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)4491 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4492 {
4493 ImGuiContext& g = *GImGui;
4494 ImGuiStyle& style = g.Style;
4495 if (window->Flags & ImGuiWindowFlags_Tooltip)
4496 {
4497 // Tooltip always resize
4498 return size_contents;
4499 }
4500 else
4501 {
4502 // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding.
4503 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4504 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4505 ImVec2 size_min = style.WindowMinSize;
4506 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)
4507 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4508 ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4509 ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4510 if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4511 size_auto_fit.y += style.ScrollbarSize;
4512 if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4513 size_auto_fit.x += style.ScrollbarSize;
4514 return size_auto_fit;
4515 }
4516 }
4517
CalcWindowExpectedSize(ImGuiWindow * window)4518 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4519 {
4520 ImVec2 size_contents = CalcSizeContents(window);
4521 return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4522 }
4523
GetWindowScrollMaxX(ImGuiWindow * window)4524 float ImGui::GetWindowScrollMaxX(ImGuiWindow* window)
4525 {
4526 return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4527 }
4528
GetWindowScrollMaxY(ImGuiWindow * window)4529 float ImGui::GetWindowScrollMaxY(ImGuiWindow* window)
4530 {
4531 return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4532 }
4533
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)4534 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4535 {
4536 ImGuiContext& g = *GImGui;
4537 ImVec2 scroll = window->Scroll;
4538 if (window->ScrollTarget.x < FLT_MAX)
4539 {
4540 float cr_x = window->ScrollTargetCenterRatio.x;
4541 scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4542 }
4543 if (window->ScrollTarget.y < FLT_MAX)
4544 {
4545 // '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.
4546 float cr_y = window->ScrollTargetCenterRatio.y;
4547 float target_y = window->ScrollTarget.y;
4548 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4549 target_y = 0.0f;
4550 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4551 target_y = window->SizeContents.y;
4552 scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4553 }
4554 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4555 if (!window->Collapsed && !window->SkipItems)
4556 {
4557 scroll.x = ImMin(scroll.x, ImGui::GetWindowScrollMaxX(window));
4558 scroll.y = ImMin(scroll.y, ImGui::GetWindowScrollMaxY(window));
4559 }
4560 return scroll;
4561 }
4562
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4563 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4564 {
4565 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4566 return ImGuiCol_PopupBg;
4567 if (flags & ImGuiWindowFlags_ChildWindow)
4568 return ImGuiCol_ChildBg;
4569 return ImGuiCol_WindowBg;
4570 }
4571
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4572 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4573 {
4574 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
4575 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4576 ImVec2 size_expected = pos_max - pos_min;
4577 ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4578 *out_pos = pos_min;
4579 if (corner_norm.x == 0.0f)
4580 out_pos->x -= (size_constrained.x - size_expected.x);
4581 if (corner_norm.y == 0.0f)
4582 out_pos->y -= (size_constrained.y - size_expected.y);
4583 *out_size = size_constrained;
4584 }
4585
4586 struct ImGuiResizeGripDef
4587 {
4588 ImVec2 CornerPos;
4589 ImVec2 InnerDir;
4590 int AngleMin12, AngleMax12;
4591 };
4592
4593 const ImGuiResizeGripDef resize_grip_def[4] =
4594 {
4595 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4596 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4597 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4598 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4599 };
4600
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4601 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4602 {
4603 ImRect rect = window->Rect();
4604 if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4605 if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness);
4606 if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding);
4607 if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness);
4608 if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding);
4609 IM_ASSERT(0);
4610 return ImRect();
4611 }
4612
4613 // Handle resize for: Resize Grips, Borders, Gamepad
UpdateManualResize(ImGuiWindow * window,const ImVec2 & size_auto_fit,int * border_held,int resize_grip_count,ImU32 resize_grip_col[4])4614 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4615 {
4616 ImGuiContext& g = *GImGui;
4617 ImGuiWindowFlags flags = window->Flags;
4618 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4619 return;
4620 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window.
4621 return;
4622
4623 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0;
4624 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4625 const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f);
4626 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f;
4627
4628 ImVec2 pos_target(FLT_MAX, FLT_MAX);
4629 ImVec2 size_target(FLT_MAX, FLT_MAX);
4630
4631 // Manual resize grips
4632 PushID("#RESIZE");
4633 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4634 {
4635 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4636 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4637
4638 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4639 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size);
4640 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4641 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4642 bool hovered, held;
4643 ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4644 //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
4645 if (hovered || held)
4646 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4647
4648 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4649 {
4650 // Manual auto-fit when double-clicking
4651 size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4652 ClearActiveID();
4653 }
4654 else if (held)
4655 {
4656 // Resize from any of the four corners
4657 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4658 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip
4659 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
4660 }
4661 if (resize_grip_n == 0 || held || hovered)
4662 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4663 }
4664 for (int border_n = 0; border_n < resize_border_count; border_n++)
4665 {
4666 bool hovered, held;
4667 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS);
4668 ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4669 //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
4670 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held)
4671 {
4672 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4673 if (held) *border_held = border_n;
4674 }
4675 if (held)
4676 {
4677 ImVec2 border_target = window->Pos;
4678 ImVec2 border_posn;
4679 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); }
4680 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); }
4681 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); }
4682 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); }
4683 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4684 }
4685 }
4686 PopID();
4687
4688 // Navigation resize (keyboard/gamepad)
4689 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4690 {
4691 ImVec2 nav_resize_delta;
4692 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4693 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4694 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4695 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4696 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4697 {
4698 const float NAV_RESIZE_SPEED = 600.0f;
4699 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4700 g.NavWindowingToggleLayer = false;
4701 g.NavDisableMouseHover = true;
4702 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4703 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4704 size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4705 }
4706 }
4707
4708 // Apply back modified position/size to window
4709 if (size_target.x != FLT_MAX)
4710 {
4711 window->SizeFull = size_target;
4712 MarkIniSettingsDirty(window);
4713 }
4714 if (pos_target.x != FLT_MAX)
4715 {
4716 window->Pos = ImFloor(pos_target);
4717 MarkIniSettingsDirty(window);
4718 }
4719
4720 window->Size = window->SizeFull;
4721 }
4722
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)4723 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4724 {
4725 window->ParentWindow = parent_window;
4726 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4727 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4728 window->RootWindow = parent_window->RootWindow;
4729 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4730 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4731 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4732 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4733 }
4734
4735 // Push a new ImGui window to add widgets to.
4736 // - 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.
4737 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4738 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4739 // 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.
4740 // - 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.
4741 // - 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)4742 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4743 {
4744 ImGuiContext& g = *GImGui;
4745 const ImGuiStyle& style = g.Style;
4746 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required
4747 IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame()
4748 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4749
4750 // Find or create
4751 ImGuiWindow* window = FindWindowByName(name);
4752 const bool window_just_created = (window == NULL);
4753 if (window_just_created)
4754 {
4755 ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here.
4756 window = CreateNewWindow(name, size_on_first_use, flags);
4757 }
4758
4759 // Automatically disable manual moving/resizing when NoInputs is set
4760 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
4761 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4762
4763 if (flags & ImGuiWindowFlags_NavFlattened)
4764 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4765
4766 const int current_frame = g.FrameCount;
4767 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4768
4769 // Update Flags, LastFrameActive, BeginOrderXXX fields
4770 if (first_begin_of_the_frame)
4771 window->Flags = (ImGuiWindowFlags)flags;
4772 else
4773 flags = window->Flags;
4774
4775 // 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
4776 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4777 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4778 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4779 window->HasCloseButton = (p_open != NULL);
4780
4781 // Update the Appearing flag
4782 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4783 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4784 if (flags & ImGuiWindowFlags_Popup)
4785 {
4786 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4787 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4788 window_just_activated_by_user |= (window != popup_ref.Window);
4789 }
4790 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4791 if (window->Appearing)
4792 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4793
4794 // Add to stack
4795 g.CurrentWindowStack.push_back(window);
4796 SetCurrentWindow(window);
4797 CheckStacksSize(window, true);
4798 if (flags & ImGuiWindowFlags_Popup)
4799 {
4800 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
4801 popup_ref.Window = window;
4802 g.BeginPopupStack.push_back(popup_ref);
4803 window->PopupId = popup_ref.PopupId;
4804 }
4805
4806 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4807 window->NavLastIds[0] = 0;
4808
4809 // Process SetNextWindow***() calls
4810 bool window_pos_set_by_api = false;
4811 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4812 if (g.NextWindowData.PosCond)
4813 {
4814 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4815 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4816 {
4817 // May be processed on the next frame if this is our first frame and we are measuring size
4818 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4819 window->SetWindowPosVal = g.NextWindowData.PosVal;
4820 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4821 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4822 }
4823 else
4824 {
4825 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4826 }
4827 }
4828 if (g.NextWindowData.SizeCond)
4829 {
4830 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4831 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4832 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4833 }
4834 if (g.NextWindowData.ContentSizeCond)
4835 {
4836 // Adjust passed "client size" to become a "window size"
4837 window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4838 if (window->SizeContentsExplicit.y != 0.0f)
4839 window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4840 }
4841 else if (first_begin_of_the_frame)
4842 {
4843 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4844 }
4845 if (g.NextWindowData.CollapsedCond)
4846 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4847 if (g.NextWindowData.FocusCond)
4848 FocusWindow(window);
4849 if (window->Appearing)
4850 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4851
4852 // When reusing window again multiple times a frame, just append content (don't need to setup again)
4853 if (first_begin_of_the_frame)
4854 {
4855 // Initialize
4856 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4857 UpdateWindowParentAndRootLinks(window, flags, parent_window);
4858
4859 window->Active = true;
4860 window->BeginOrderWithinParent = 0;
4861 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
4862 window->BeginCount = 0;
4863 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4864 window->LastFrameActive = current_frame;
4865 window->IDStack.resize(1);
4866
4867 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
4868 // 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.
4869 bool window_title_visible_elsewhere = false;
4870 if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB
4871 window_title_visible_elsewhere = true;
4872 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
4873 {
4874 size_t buf_len = (size_t)window->NameBufLen;
4875 window->Name = ImStrdupcpy(window->Name, &buf_len, name);
4876 window->NameBufLen = (int)buf_len;
4877 }
4878
4879 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
4880
4881 // Update contents size from last frame for auto-fitting (or use explicit size)
4882 window->SizeContents = CalcSizeContents(window);
4883 if (window->HiddenFramesRegular > 0)
4884 window->HiddenFramesRegular--;
4885 if (window->HiddenFramesForResize > 0)
4886 window->HiddenFramesForResize--;
4887
4888 // Hide new windows for one frame until they calculate their size
4889 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
4890 window->HiddenFramesForResize = 1;
4891
4892 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
4893 // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
4894 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
4895 {
4896 window->HiddenFramesForResize = 1;
4897 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4898 {
4899 if (!window_size_x_set_by_api)
4900 window->Size.x = window->SizeFull.x = 0.f;
4901 if (!window_size_y_set_by_api)
4902 window->Size.y = window->SizeFull.y = 0.f;
4903 window->SizeContents = ImVec2(0.f, 0.f);
4904 }
4905 }
4906
4907 SetCurrentWindow(window);
4908
4909 // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
4910 window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4911 window->WindowPadding = style.WindowPadding;
4912 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4913 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4914 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
4915 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
4916
4917 // Collapse window by double-clicking on title bar
4918 // 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
4919 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4920 {
4921 // 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.
4922 ImRect title_bar_rect = window->TitleBarRect();
4923 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4924 window->WantCollapseToggle = true;
4925 if (window->WantCollapseToggle)
4926 {
4927 window->Collapsed = !window->Collapsed;
4928 MarkIniSettingsDirty(window);
4929 FocusWindow(window);
4930 }
4931 }
4932 else
4933 {
4934 window->Collapsed = false;
4935 }
4936 window->WantCollapseToggle = false;
4937
4938 // SIZE
4939
4940 // Calculate auto-fit size, handle automatic resize
4941 const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
4942 ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
4943 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
4944 {
4945 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
4946 if (!window_size_x_set_by_api)
4947 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
4948 if (!window_size_y_set_by_api)
4949 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
4950 }
4951 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4952 {
4953 // Auto-fit may only grow window during the first few frames
4954 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4955 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
4956 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4957 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
4958 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4959 if (!window->Collapsed)
4960 MarkIniSettingsDirty(window);
4961 }
4962
4963 // Apply minimum/maximum window size constraints and final size
4964 window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
4965 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
4966
4967 // SCROLLBAR STATUS
4968
4969 // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
4970 if (!window->Collapsed)
4971 {
4972 // When reading the current size we need to read it after size constraints have been applied
4973 float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
4974 float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
4975 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
4976 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4977 if (window->ScrollbarX && !window->ScrollbarY)
4978 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4979 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4980 }
4981
4982 // POSITION
4983
4984 // Popup latch its initial position, will position itself when it appears next frame
4985 if (window_just_activated_by_user)
4986 {
4987 window->AutoPosLastDirection = ImGuiDir_None;
4988 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4989 window->Pos = g.BeginPopupStack.back().OpenPopupPos;
4990 }
4991
4992 // Position child window
4993 if (flags & ImGuiWindowFlags_ChildWindow)
4994 {
4995 IM_ASSERT(parent_window->Active);
4996 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
4997 parent_window->DC.ChildWindows.push_back(window);
4998 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
4999 window->Pos = parent_window->DC.CursorPos;
5000 }
5001
5002 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
5003 if (window_pos_with_pivot)
5004 SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
5005 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
5006 window->Pos = FindBestWindowPosForPopup(window);
5007 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
5008 window->Pos = FindBestWindowPosForPopup(window);
5009 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
5010 window->Pos = FindBestWindowPosForPopup(window);
5011
5012 // Clamp position so it stays visible
5013 if (!(flags & ImGuiWindowFlags_ChildWindow))
5014 {
5015 if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && 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.
5016 {
5017 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
5018 ImVec2 size_for_clamping = ((g.IO.ConfigWindowsMoveFromTitleBarOnly) && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size;
5019 window->Pos = ImMax(window->Pos + size_for_clamping, padding) - size_for_clamping;
5020 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
5021 }
5022 }
5023 window->Pos = ImFloor(window->Pos);
5024
5025 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
5026 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
5027
5028 // Prepare for item focus requests
5029 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
5030 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
5031 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
5032 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
5033
5034 // Apply scrolling
5035 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
5036 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
5037
5038 // Apply window focus (new and reactivated windows are moved to front)
5039 bool want_focus = false;
5040 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
5041 if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
5042 want_focus = true;
5043
5044 // Handle manual resize: Resize Grips, Borders, Gamepad
5045 int border_held = -1;
5046 ImU32 resize_grip_col[4] = { 0 };
5047 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4
5048 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
5049 if (!window->Collapsed)
5050 UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
5051
5052 // Default item width. Make it proportional to window size if window manually resizes
5053 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
5054 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
5055 else
5056 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
5057
5058 // DRAWING
5059
5060 // Setup draw list and outer clipping rectangle
5061 window->DrawList->Clear();
5062 window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
5063 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
5064 ImRect viewport_rect(GetViewportRect());
5065 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
5066 PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
5067 else
5068 PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
5069
5070 // Draw modal window background (darkens what is behind them, all viewports)
5071 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
5072 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
5073 if (dim_bg_for_modal || dim_bg_for_window_list)
5074 {
5075 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
5076 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
5077 }
5078
5079 // Draw navigation selection/windowing rectangle background
5080 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
5081 {
5082 ImRect bb = window->Rect();
5083 bb.Expand(g.FontSize);
5084 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
5085 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
5086 }
5087
5088 // Draw window + handle manual resize
5089 const float window_rounding = window->WindowRounding;
5090 const float window_border_size = window->WindowBorderSize;
5091 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
5092 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
5093 const ImRect title_bar_rect = window->TitleBarRect();
5094 if (window->Collapsed)
5095 {
5096 // Title bar only
5097 float backup_border_size = style.FrameBorderSize;
5098 g.Style.FrameBorderSize = window->WindowBorderSize;
5099 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
5100 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
5101 g.Style.FrameBorderSize = backup_border_size;
5102 }
5103 else
5104 {
5105 // Window background
5106 if (!(flags & ImGuiWindowFlags_NoBackground))
5107 {
5108 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
5109 if (g.NextWindowData.BgAlphaCond != 0)
5110 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
5111 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
5112 }
5113 g.NextWindowData.BgAlphaCond = 0;
5114
5115 // Title bar
5116 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5117 {
5118 ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
5119 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
5120 }
5121
5122 // Menu bar
5123 if (flags & ImGuiWindowFlags_MenuBar)
5124 {
5125 ImRect menu_bar_rect = window->MenuBarRect();
5126 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.
5127 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
5128 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
5129 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5130 }
5131
5132 // Scrollbars
5133 if (window->ScrollbarX)
5134 Scrollbar(ImGuiLayoutType_Horizontal);
5135 if (window->ScrollbarY)
5136 Scrollbar(ImGuiLayoutType_Vertical);
5137
5138 // Render resize grips (after their input handling so we don't have a frame of latency)
5139 if (!(flags & ImGuiWindowFlags_NoResize))
5140 {
5141 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
5142 {
5143 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
5144 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
5145 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
5146 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
5147 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);
5148 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
5149 }
5150 }
5151
5152 // Borders
5153 if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground))
5154 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
5155 if (border_held != -1)
5156 {
5157 ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f);
5158 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
5159 }
5160 if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
5161 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
5162 }
5163
5164 // Draw navigation selection/windowing rectangle border
5165 if (g.NavWindowingTargetAnim == window)
5166 {
5167 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
5168 ImRect bb = window->Rect();
5169 bb.Expand(g.FontSize);
5170 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
5171 {
5172 bb.Expand(-g.FontSize - 1.0f);
5173 rounding = window->WindowRounding;
5174 }
5175 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
5176 }
5177
5178 // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
5179 window->SizeFullAtLastBegin = window->SizeFull;
5180
5181 // Update various regions. Variables they depends on are set above in this function.
5182 // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
5183 window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
5184 window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
5185 window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x));
5186 window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y));
5187
5188 // Setup drawing context
5189 // (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.)
5190 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
5191 window->DC.GroupOffset.x = 0.0f;
5192 window->DC.ColumnsOffset.x = 0.0f;
5193 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
5194 window->DC.CursorPos = window->DC.CursorStartPos;
5195 window->DC.CursorPosPrevLine = window->DC.CursorPos;
5196 window->DC.CursorMaxPos = window->DC.CursorStartPos;
5197 window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
5198 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
5199 window->DC.NavHideHighlightOneFrame = false;
5200 window->DC.NavHasScroll = (GetWindowScrollMaxY(window) > 0.0f);
5201 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
5202 window->DC.NavLayerActiveMaskNext = 0x00;
5203 window->DC.MenuBarAppending = false;
5204 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
5205 window->DC.ChildWindows.resize(0);
5206 window->DC.LayoutType = ImGuiLayoutType_Vertical;
5207 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
5208 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
5209 window->DC.ItemWidth = window->ItemWidthDefault;
5210 window->DC.TextWrapPos = -1.0f; // disabled
5211 window->DC.ItemFlagsStack.resize(0);
5212 window->DC.ItemWidthStack.resize(0);
5213 window->DC.TextWrapPosStack.resize(0);
5214 window->DC.ColumnsSet = NULL;
5215 window->DC.TreeDepth = 0;
5216 window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
5217 window->DC.StateStorage = &window->StateStorage;
5218 window->DC.GroupStack.resize(0);
5219 window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
5220
5221 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
5222 {
5223 window->DC.ItemFlags = parent_window->DC.ItemFlags;
5224 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5225 }
5226
5227 if (window->AutoFitFramesX > 0)
5228 window->AutoFitFramesX--;
5229 if (window->AutoFitFramesY > 0)
5230 window->AutoFitFramesY--;
5231
5232 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
5233 if (want_focus)
5234 {
5235 FocusWindow(window);
5236 NavInitWindow(window, false);
5237 }
5238
5239 // Title bar
5240 if (!(flags & ImGuiWindowFlags_NoTitleBar))
5241 {
5242 // Close & collapse button are on layer 1 (same as menus) and don't default focus
5243 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
5244 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
5245 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
5246 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu);
5247
5248 // Collapse button
5249 if (!(flags & ImGuiWindowFlags_NoCollapse))
5250 if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
5251 window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
5252
5253 // Close button
5254 if (p_open != NULL)
5255 {
5256 const float pad = style.FramePadding.y;
5257 const float rad = g.FontSize * 0.5f;
5258 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
5259 *p_open = false;
5260 }
5261
5262 window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
5263 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main);
5264 window->DC.ItemFlags = item_flags_backup;
5265
5266 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
5267 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code..
5268 const char* UNSAVED_DOCUMENT_MARKER = "*";
5269 float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f;
5270 ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
5271 ImRect text_r = title_bar_rect;
5272 float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5273 float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5274 if (style.WindowTitleAlign.x > 0.0f)
5275 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5276 text_r.Min.x += pad_left;
5277 text_r.Max.x -= pad_right;
5278 ImRect clip_rect = text_r;
5279 clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton()
5280 RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5281 if (flags & ImGuiWindowFlags_UnsavedDocument)
5282 {
5283 ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f);
5284 ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f));
5285 RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect);
5286 }
5287 }
5288
5289 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5290 window->OuterRectClipped = window->Rect();
5291 window->OuterRectClipped.ClipWith(window->ClipRect);
5292
5293 // Pressing CTRL+C while holding on a window copy its content to the clipboard
5294 // 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.
5295 // Maybe we can support CTRL+C on every element?
5296 /*
5297 if (g.ActiveId == move_id)
5298 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5299 LogToClipboard();
5300 */
5301
5302 // Inner rectangle
5303 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5304 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5305 window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5306 window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5307 window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5308 window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5309 //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5310
5311 // Inner clipping rectangle
5312 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5313 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5314 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5315 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5316 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5317
5318 // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.).
5319 window->DC.LastItemId = window->MoveId;
5320 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5321 window->DC.LastItemRect = title_bar_rect;
5322 }
5323
5324 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5325
5326 // 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)
5327 if (first_begin_of_the_frame)
5328 window->WriteAccessed = false;
5329
5330 window->BeginCount++;
5331 g.NextWindowData.Clear();
5332
5333 if (flags & ImGuiWindowFlags_ChildWindow)
5334 {
5335 // Child window can be out of sight and have "negative" clip windows.
5336 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5337 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5338
5339 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5340 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5341 window->HiddenFramesRegular = 1;
5342
5343 // Completely hide along with parent or if parent is collapsed
5344 if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5345 window->HiddenFramesRegular = 1;
5346 }
5347
5348 // 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)
5349 if (style.Alpha <= 0.0f)
5350 window->HiddenFramesRegular = 1;
5351
5352 // Update the Hidden flag
5353 window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0);
5354
5355 // Return false if we don't intend to display anything to allow user to perform an early out optimization
5356 window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5357
5358 return !window->SkipItems;
5359 }
5360
5361 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5362 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)5363 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5364 {
5365 // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file.
5366 if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5367 SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5368
5369 // Old API feature: override the window background alpha with a parameter.
5370 if (bg_alpha_override >= 0.0f)
5371 SetNextWindowBgAlpha(bg_alpha_override);
5372
5373 return Begin(name, p_open, flags);
5374 }
5375 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5376
End()5377 void ImGui::End()
5378 {
5379 ImGuiContext& g = *GImGui;
5380
5381 if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow)
5382 {
5383 IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!");
5384 return; // FIXME-ERRORHANDLING
5385 }
5386 IM_ASSERT(g.CurrentWindowStack.Size > 0);
5387
5388 ImGuiWindow* window = g.CurrentWindow;
5389
5390 if (window->DC.ColumnsSet != NULL)
5391 EndColumns();
5392 PopClipRect(); // Inner window clip rectangle
5393
5394 // Stop logging
5395 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5396 LogFinish();
5397
5398 // Pop from window stack
5399 g.CurrentWindowStack.pop_back();
5400 if (window->Flags & ImGuiWindowFlags_Popup)
5401 g.BeginPopupStack.pop_back();
5402 CheckStacksSize(window, false);
5403 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5404 }
5405
BringWindowToFocusFront(ImGuiWindow * window)5406 void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
5407 {
5408 ImGuiContext& g = *GImGui;
5409 if (g.WindowsFocusOrder.back() == window)
5410 return;
5411 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window
5412 if (g.WindowsFocusOrder[i] == window)
5413 {
5414 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*));
5415 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window;
5416 break;
5417 }
5418 }
5419
BringWindowToDisplayFront(ImGuiWindow * window)5420 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
5421 {
5422 ImGuiContext& g = *GImGui;
5423 ImGuiWindow* current_front_window = g.Windows.back();
5424 if (current_front_window == window || current_front_window->RootWindow == window)
5425 return;
5426 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5427 if (g.Windows[i] == window)
5428 {
5429 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
5430 g.Windows[g.Windows.Size - 1] = window;
5431 break;
5432 }
5433 }
5434
BringWindowToDisplayBack(ImGuiWindow * window)5435 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
5436 {
5437 ImGuiContext& g = *GImGui;
5438 if (g.Windows[0] == window)
5439 return;
5440 for (int i = 0; i < g.Windows.Size; i++)
5441 if (g.Windows[i] == window)
5442 {
5443 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5444 g.Windows[0] = window;
5445 break;
5446 }
5447 }
5448
5449 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5450 void ImGui::FocusWindow(ImGuiWindow* window)
5451 {
5452 ImGuiContext& g = *GImGui;
5453
5454 if (g.NavWindow != window)
5455 {
5456 g.NavWindow = window;
5457 if (window && g.NavDisableMouseHover)
5458 g.NavMousePosDirty = true;
5459 g.NavInitRequest = false;
5460 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5461 g.NavIdIsAlive = false;
5462 g.NavLayer = ImGuiNavLayer_Main;
5463 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
5464 }
5465
5466 // Passing NULL allow to disable keyboard focus
5467 if (!window)
5468 return;
5469
5470 // Move the root window to the top of the pile
5471 if (window->RootWindow)
5472 window = window->RootWindow;
5473
5474 // Steal focus on active widgets
5475 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5476 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5477 ClearActiveID();
5478
5479 // Bring to front
5480 BringWindowToFocusFront(window);
5481 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5482 BringWindowToDisplayFront(window);
5483 }
5484
FocusPreviousWindowIgnoringOne(ImGuiWindow * ignore_window)5485 void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window)
5486 {
5487 ImGuiContext& g = *GImGui;
5488 for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--)
5489 {
5490 // 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.
5491 ImGuiWindow* window = g.WindowsFocusOrder[i];
5492 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow))
5493 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
5494 {
5495 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window);
5496 FocusWindow(focus_window);
5497 return;
5498 }
5499 }
5500 }
5501
PushItemWidth(float item_width)5502 void ImGui::PushItemWidth(float item_width)
5503 {
5504 ImGuiWindow* window = GetCurrentWindow();
5505 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5506 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5507 }
5508
PushMultiItemsWidths(int components,float w_full)5509 void ImGui::PushMultiItemsWidths(int components, float w_full)
5510 {
5511 ImGuiWindow* window = GetCurrentWindow();
5512 const ImGuiStyle& style = GImGui->Style;
5513 if (w_full <= 0.0f)
5514 w_full = CalcItemWidth();
5515 const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5516 const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5517 window->DC.ItemWidthStack.push_back(w_item_last);
5518 for (int i = 0; i < components-1; i++)
5519 window->DC.ItemWidthStack.push_back(w_item_one);
5520 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5521 }
5522
PopItemWidth()5523 void ImGui::PopItemWidth()
5524 {
5525 ImGuiWindow* window = GetCurrentWindow();
5526 window->DC.ItemWidthStack.pop_back();
5527 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5528 }
5529
CalcItemWidth()5530 float ImGui::CalcItemWidth()
5531 {
5532 ImGuiWindow* window = GetCurrentWindowRead();
5533 float w = window->DC.ItemWidth;
5534 if (w < 0.0f)
5535 {
5536 // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well.
5537 float width_to_right_edge = GetContentRegionAvail().x;
5538 w = ImMax(1.0f, width_to_right_edge + w);
5539 }
5540 w = (float)(int)w;
5541 return w;
5542 }
5543
SetCurrentFont(ImFont * font)5544 void ImGui::SetCurrentFont(ImFont* font)
5545 {
5546 ImGuiContext& g = *GImGui;
5547 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5548 IM_ASSERT(font->Scale > 0.0f);
5549 g.Font = font;
5550 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
5551 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5552
5553 ImFontAtlas* atlas = g.Font->ContainerAtlas;
5554 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5555 g.DrawListSharedData.Font = g.Font;
5556 g.DrawListSharedData.FontSize = g.FontSize;
5557 }
5558
PushFont(ImFont * font)5559 void ImGui::PushFont(ImFont* font)
5560 {
5561 ImGuiContext& g = *GImGui;
5562 if (!font)
5563 font = GetDefaultFont();
5564 SetCurrentFont(font);
5565 g.FontStack.push_back(font);
5566 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5567 }
5568
PopFont()5569 void ImGui::PopFont()
5570 {
5571 ImGuiContext& g = *GImGui;
5572 g.CurrentWindow->DrawList->PopTextureID();
5573 g.FontStack.pop_back();
5574 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5575 }
5576
PushItemFlag(ImGuiItemFlags option,bool enabled)5577 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5578 {
5579 ImGuiWindow* window = GetCurrentWindow();
5580 if (enabled)
5581 window->DC.ItemFlags |= option;
5582 else
5583 window->DC.ItemFlags &= ~option;
5584 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5585 }
5586
PopItemFlag()5587 void ImGui::PopItemFlag()
5588 {
5589 ImGuiWindow* window = GetCurrentWindow();
5590 window->DC.ItemFlagsStack.pop_back();
5591 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5592 }
5593
5594 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system.
PushAllowKeyboardFocus(bool allow_keyboard_focus)5595 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5596 {
5597 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus);
5598 }
5599
PopAllowKeyboardFocus()5600 void ImGui::PopAllowKeyboardFocus()
5601 {
5602 PopItemFlag();
5603 }
5604
PushButtonRepeat(bool repeat)5605 void ImGui::PushButtonRepeat(bool repeat)
5606 {
5607 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5608 }
5609
PopButtonRepeat()5610 void ImGui::PopButtonRepeat()
5611 {
5612 PopItemFlag();
5613 }
5614
PushTextWrapPos(float wrap_pos_x)5615 void ImGui::PushTextWrapPos(float wrap_pos_x)
5616 {
5617 ImGuiWindow* window = GetCurrentWindow();
5618 window->DC.TextWrapPos = wrap_pos_x;
5619 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5620 }
5621
PopTextWrapPos()5622 void ImGui::PopTextWrapPos()
5623 {
5624 ImGuiWindow* window = GetCurrentWindow();
5625 window->DC.TextWrapPosStack.pop_back();
5626 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5627 }
5628
5629 // 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)5630 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5631 {
5632 ImGuiContext& g = *GImGui;
5633 ImGuiColorMod backup;
5634 backup.Col = idx;
5635 backup.BackupValue = g.Style.Colors[idx];
5636 g.ColorModifiers.push_back(backup);
5637 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5638 }
5639
PushStyleColor(ImGuiCol idx,const ImVec4 & col)5640 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5641 {
5642 ImGuiContext& g = *GImGui;
5643 ImGuiColorMod backup;
5644 backup.Col = idx;
5645 backup.BackupValue = g.Style.Colors[idx];
5646 g.ColorModifiers.push_back(backup);
5647 g.Style.Colors[idx] = col;
5648 }
5649
PopStyleColor(int count)5650 void ImGui::PopStyleColor(int count)
5651 {
5652 ImGuiContext& g = *GImGui;
5653 while (count > 0)
5654 {
5655 ImGuiColorMod& backup = g.ColorModifiers.back();
5656 g.Style.Colors[backup.Col] = backup.BackupValue;
5657 g.ColorModifiers.pop_back();
5658 count--;
5659 }
5660 }
5661
5662 struct ImGuiStyleVarInfo
5663 {
5664 ImGuiDataType Type;
5665 ImU32 Count;
5666 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo5667 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5668 };
5669
5670 static const ImGuiStyleVarInfo GStyleVarInfo[] =
5671 {
5672 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
5673 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
5674 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
5675 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
5676 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
5677 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
5678 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
5679 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
5680 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
5681 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
5682 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
5683 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
5684 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
5685 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
5686 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
5687 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
5688 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
5689 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
5690 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
5691 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
5692 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding
5693 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
5694 };
5695
GetStyleVarInfo(ImGuiStyleVar idx)5696 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5697 {
5698 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5699 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5700 return &GStyleVarInfo[idx];
5701 }
5702
PushStyleVar(ImGuiStyleVar idx,float val)5703 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5704 {
5705 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5706 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5707 {
5708 ImGuiContext& g = *GImGui;
5709 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5710 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5711 *pvar = val;
5712 return;
5713 }
5714 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5715 }
5716
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)5717 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5718 {
5719 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5720 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5721 {
5722 ImGuiContext& g = *GImGui;
5723 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5724 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5725 *pvar = val;
5726 return;
5727 }
5728 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5729 }
5730
PopStyleVar(int count)5731 void ImGui::PopStyleVar(int count)
5732 {
5733 ImGuiContext& g = *GImGui;
5734 while (count > 0)
5735 {
5736 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5737 ImGuiStyleMod& backup = g.StyleModifiers.back();
5738 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5739 void* data = info->GetVarPtr(&g.Style);
5740 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
5741 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5742 g.StyleModifiers.pop_back();
5743 count--;
5744 }
5745 }
5746
GetStyleColorName(ImGuiCol idx)5747 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5748 {
5749 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5750 switch (idx)
5751 {
5752 case ImGuiCol_Text: return "Text";
5753 case ImGuiCol_TextDisabled: return "TextDisabled";
5754 case ImGuiCol_WindowBg: return "WindowBg";
5755 case ImGuiCol_ChildBg: return "ChildBg";
5756 case ImGuiCol_PopupBg: return "PopupBg";
5757 case ImGuiCol_Border: return "Border";
5758 case ImGuiCol_BorderShadow: return "BorderShadow";
5759 case ImGuiCol_FrameBg: return "FrameBg";
5760 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5761 case ImGuiCol_FrameBgActive: return "FrameBgActive";
5762 case ImGuiCol_TitleBg: return "TitleBg";
5763 case ImGuiCol_TitleBgActive: return "TitleBgActive";
5764 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5765 case ImGuiCol_MenuBarBg: return "MenuBarBg";
5766 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5767 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5768 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5769 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5770 case ImGuiCol_CheckMark: return "CheckMark";
5771 case ImGuiCol_SliderGrab: return "SliderGrab";
5772 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5773 case ImGuiCol_Button: return "Button";
5774 case ImGuiCol_ButtonHovered: return "ButtonHovered";
5775 case ImGuiCol_ButtonActive: return "ButtonActive";
5776 case ImGuiCol_Header: return "Header";
5777 case ImGuiCol_HeaderHovered: return "HeaderHovered";
5778 case ImGuiCol_HeaderActive: return "HeaderActive";
5779 case ImGuiCol_Separator: return "Separator";
5780 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5781 case ImGuiCol_SeparatorActive: return "SeparatorActive";
5782 case ImGuiCol_ResizeGrip: return "ResizeGrip";
5783 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5784 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5785 case ImGuiCol_Tab: return "Tab";
5786 case ImGuiCol_TabHovered: return "TabHovered";
5787 case ImGuiCol_TabActive: return "TabActive";
5788 case ImGuiCol_TabUnfocused: return "TabUnfocused";
5789 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
5790 case ImGuiCol_PlotLines: return "PlotLines";
5791 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5792 case ImGuiCol_PlotHistogram: return "PlotHistogram";
5793 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5794 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5795 case ImGuiCol_DragDropTarget: return "DragDropTarget";
5796 case ImGuiCol_NavHighlight: return "NavHighlight";
5797 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5798 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5799 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5800 }
5801 IM_ASSERT(0);
5802 return "Unknown";
5803 }
5804
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)5805 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5806 {
5807 if (window->RootWindow == potential_parent)
5808 return true;
5809 while (window != NULL)
5810 {
5811 if (window == potential_parent)
5812 return true;
5813 window = window->ParentWindow;
5814 }
5815 return false;
5816 }
5817
IsWindowHovered(ImGuiHoveredFlags flags)5818 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5819 {
5820 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
5821 ImGuiContext& g = *GImGui;
5822
5823 if (flags & ImGuiHoveredFlags_AnyWindow)
5824 {
5825 if (g.HoveredWindow == NULL)
5826 return false;
5827 }
5828 else
5829 {
5830 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5831 {
5832 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5833 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5834 return false;
5835 break;
5836 case ImGuiHoveredFlags_RootWindow:
5837 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5838 return false;
5839 break;
5840 case ImGuiHoveredFlags_ChildWindows:
5841 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5842 return false;
5843 break;
5844 default:
5845 if (g.HoveredWindow != g.CurrentWindow)
5846 return false;
5847 break;
5848 }
5849 }
5850
5851 if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5852 return false;
5853 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5854 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5855 return false;
5856 return true;
5857 }
5858
IsWindowFocused(ImGuiFocusedFlags flags)5859 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5860 {
5861 ImGuiContext& g = *GImGui;
5862
5863 if (flags & ImGuiFocusedFlags_AnyWindow)
5864 return g.NavWindow != NULL;
5865
5866 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5867 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5868 {
5869 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5870 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5871 case ImGuiFocusedFlags_RootWindow:
5872 return g.NavWindow == g.CurrentWindow->RootWindow;
5873 case ImGuiFocusedFlags_ChildWindows:
5874 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
5875 default:
5876 return g.NavWindow == g.CurrentWindow;
5877 }
5878 }
5879
5880 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
5881 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly.
5882 // If you want a window to never be focused, you may use the e.g. NoInputs flag.
IsWindowNavFocusable(ImGuiWindow * window)5883 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
5884 {
5885 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
5886 }
5887
GetWindowWidth()5888 float ImGui::GetWindowWidth()
5889 {
5890 ImGuiWindow* window = GImGui->CurrentWindow;
5891 return window->Size.x;
5892 }
5893
GetWindowHeight()5894 float ImGui::GetWindowHeight()
5895 {
5896 ImGuiWindow* window = GImGui->CurrentWindow;
5897 return window->Size.y;
5898 }
5899
GetWindowPos()5900 ImVec2 ImGui::GetWindowPos()
5901 {
5902 ImGuiContext& g = *GImGui;
5903 ImGuiWindow* window = g.CurrentWindow;
5904 return window->Pos;
5905 }
5906
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)5907 void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
5908 {
5909 window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5910 window->Scroll.x = new_scroll_x;
5911 window->DC.CursorMaxPos.x -= window->Scroll.x;
5912 }
5913
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)5914 void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5915 {
5916 window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it.
5917 window->Scroll.y = new_scroll_y;
5918 window->DC.CursorMaxPos.y -= window->Scroll.y;
5919 }
5920
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)5921 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5922 {
5923 // Test condition (NB: bit 0 is always true) and clear flags for next time
5924 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5925 return;
5926
5927 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5928 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5929 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5930
5931 // Set
5932 const ImVec2 old_pos = window->Pos;
5933 window->Pos = ImFloor(pos);
5934 window->DC.CursorPos += (window->Pos - old_pos); // 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
5935 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5936 }
5937
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)5938 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5939 {
5940 ImGuiWindow* window = GetCurrentWindowRead();
5941 SetWindowPos(window, pos, cond);
5942 }
5943
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)5944 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5945 {
5946 if (ImGuiWindow* window = FindWindowByName(name))
5947 SetWindowPos(window, pos, cond);
5948 }
5949
GetWindowSize()5950 ImVec2 ImGui::GetWindowSize()
5951 {
5952 ImGuiWindow* window = GetCurrentWindowRead();
5953 return window->Size;
5954 }
5955
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)5956 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5957 {
5958 // Test condition (NB: bit 0 is always true) and clear flags for next time
5959 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5960 return;
5961
5962 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5963 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5964
5965 // Set
5966 if (size.x > 0.0f)
5967 {
5968 window->AutoFitFramesX = 0;
5969 window->SizeFull.x = ImFloor(size.x);
5970 }
5971 else
5972 {
5973 window->AutoFitFramesX = 2;
5974 window->AutoFitOnlyGrows = false;
5975 }
5976 if (size.y > 0.0f)
5977 {
5978 window->AutoFitFramesY = 0;
5979 window->SizeFull.y = ImFloor(size.y);
5980 }
5981 else
5982 {
5983 window->AutoFitFramesY = 2;
5984 window->AutoFitOnlyGrows = false;
5985 }
5986 }
5987
SetWindowSize(const ImVec2 & size,ImGuiCond cond)5988 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5989 {
5990 SetWindowSize(GImGui->CurrentWindow, size, cond);
5991 }
5992
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)5993 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5994 {
5995 if (ImGuiWindow* window = FindWindowByName(name))
5996 SetWindowSize(window, size, cond);
5997 }
5998
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)5999 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
6000 {
6001 // Test condition (NB: bit 0 is always true) and clear flags for next time
6002 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
6003 return;
6004 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
6005
6006 // Set
6007 window->Collapsed = collapsed;
6008 }
6009
SetWindowCollapsed(bool collapsed,ImGuiCond cond)6010 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
6011 {
6012 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
6013 }
6014
IsWindowCollapsed()6015 bool ImGui::IsWindowCollapsed()
6016 {
6017 ImGuiWindow* window = GetCurrentWindowRead();
6018 return window->Collapsed;
6019 }
6020
IsWindowAppearing()6021 bool ImGui::IsWindowAppearing()
6022 {
6023 ImGuiWindow* window = GetCurrentWindowRead();
6024 return window->Appearing;
6025 }
6026
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)6027 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
6028 {
6029 if (ImGuiWindow* window = FindWindowByName(name))
6030 SetWindowCollapsed(window, collapsed, cond);
6031 }
6032
SetWindowFocus()6033 void ImGui::SetWindowFocus()
6034 {
6035 FocusWindow(GImGui->CurrentWindow);
6036 }
6037
SetWindowFocus(const char * name)6038 void ImGui::SetWindowFocus(const char* name)
6039 {
6040 if (name)
6041 {
6042 if (ImGuiWindow* window = FindWindowByName(name))
6043 FocusWindow(window);
6044 }
6045 else
6046 {
6047 FocusWindow(NULL);
6048 }
6049 }
6050
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)6051 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
6052 {
6053 ImGuiContext& g = *GImGui;
6054 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6055 g.NextWindowData.PosVal = pos;
6056 g.NextWindowData.PosPivotVal = pivot;
6057 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
6058 }
6059
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)6060 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
6061 {
6062 ImGuiContext& g = *GImGui;
6063 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6064 g.NextWindowData.SizeVal = size;
6065 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
6066 }
6067
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)6068 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
6069 {
6070 ImGuiContext& g = *GImGui;
6071 g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
6072 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
6073 g.NextWindowData.SizeCallback = custom_callback;
6074 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
6075 }
6076
SetNextWindowContentSize(const ImVec2 & size)6077 void ImGui::SetNextWindowContentSize(const ImVec2& size)
6078 {
6079 ImGuiContext& g = *GImGui;
6080 g.NextWindowData.ContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value.
6081 g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
6082 }
6083
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)6084 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
6085 {
6086 ImGuiContext& g = *GImGui;
6087 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
6088 g.NextWindowData.CollapsedVal = collapsed;
6089 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
6090 }
6091
SetNextWindowFocus()6092 void ImGui::SetNextWindowFocus()
6093 {
6094 ImGuiContext& g = *GImGui;
6095 g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6096 }
6097
SetNextWindowBgAlpha(float alpha)6098 void ImGui::SetNextWindowBgAlpha(float alpha)
6099 {
6100 ImGuiContext& g = *GImGui;
6101 g.NextWindowData.BgAlphaVal = alpha;
6102 g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
6103 }
6104
6105 // In window space (not screen space!)
GetContentRegionMax()6106 ImVec2 ImGui::GetContentRegionMax()
6107 {
6108 ImGuiWindow* window = GetCurrentWindowRead();
6109 ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
6110 if (window->DC.ColumnsSet)
6111 mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
6112 return mx;
6113 }
6114
GetContentRegionAvail()6115 ImVec2 ImGui::GetContentRegionAvail()
6116 {
6117 ImGuiWindow* window = GetCurrentWindowRead();
6118 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
6119 }
6120
GetContentRegionAvailWidth()6121 float ImGui::GetContentRegionAvailWidth()
6122 {
6123 return GetContentRegionAvail().x;
6124 }
6125
6126 // In window space (not screen space!)
GetWindowContentRegionMin()6127 ImVec2 ImGui::GetWindowContentRegionMin()
6128 {
6129 ImGuiWindow* window = GetCurrentWindowRead();
6130 return window->ContentsRegionRect.Min - window->Pos;
6131 }
6132
GetWindowContentRegionMax()6133 ImVec2 ImGui::GetWindowContentRegionMax()
6134 {
6135 ImGuiWindow* window = GetCurrentWindowRead();
6136 return window->ContentsRegionRect.Max - window->Pos;
6137 }
6138
GetWindowContentRegionWidth()6139 float ImGui::GetWindowContentRegionWidth()
6140 {
6141 ImGuiWindow* window = GetCurrentWindowRead();
6142 return window->ContentsRegionRect.GetWidth();
6143 }
6144
GetTextLineHeight()6145 float ImGui::GetTextLineHeight()
6146 {
6147 ImGuiContext& g = *GImGui;
6148 return g.FontSize;
6149 }
6150
GetTextLineHeightWithSpacing()6151 float ImGui::GetTextLineHeightWithSpacing()
6152 {
6153 ImGuiContext& g = *GImGui;
6154 return g.FontSize + g.Style.ItemSpacing.y;
6155 }
6156
GetFrameHeight()6157 float ImGui::GetFrameHeight()
6158 {
6159 ImGuiContext& g = *GImGui;
6160 return g.FontSize + g.Style.FramePadding.y * 2.0f;
6161 }
6162
GetFrameHeightWithSpacing()6163 float ImGui::GetFrameHeightWithSpacing()
6164 {
6165 ImGuiContext& g = *GImGui;
6166 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
6167 }
6168
GetWindowDrawList()6169 ImDrawList* ImGui::GetWindowDrawList()
6170 {
6171 ImGuiWindow* window = GetCurrentWindow();
6172 return window->DrawList;
6173 }
6174
GetFont()6175 ImFont* ImGui::GetFont()
6176 {
6177 return GImGui->Font;
6178 }
6179
GetFontSize()6180 float ImGui::GetFontSize()
6181 {
6182 return GImGui->FontSize;
6183 }
6184
GetFontTexUvWhitePixel()6185 ImVec2 ImGui::GetFontTexUvWhitePixel()
6186 {
6187 return GImGui->DrawListSharedData.TexUvWhitePixel;
6188 }
6189
SetWindowFontScale(float scale)6190 void ImGui::SetWindowFontScale(float scale)
6191 {
6192 ImGuiContext& g = *GImGui;
6193 ImGuiWindow* window = GetCurrentWindow();
6194 window->FontWindowScale = scale;
6195 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
6196 }
6197
6198 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
6199 // 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()6200 ImVec2 ImGui::GetCursorPos()
6201 {
6202 ImGuiWindow* window = GetCurrentWindowRead();
6203 return window->DC.CursorPos - window->Pos + window->Scroll;
6204 }
6205
GetCursorPosX()6206 float ImGui::GetCursorPosX()
6207 {
6208 ImGuiWindow* window = GetCurrentWindowRead();
6209 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
6210 }
6211
GetCursorPosY()6212 float ImGui::GetCursorPosY()
6213 {
6214 ImGuiWindow* window = GetCurrentWindowRead();
6215 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
6216 }
6217
SetCursorPos(const ImVec2 & local_pos)6218 void ImGui::SetCursorPos(const ImVec2& local_pos)
6219 {
6220 ImGuiWindow* window = GetCurrentWindow();
6221 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
6222 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6223 }
6224
SetCursorPosX(float x)6225 void ImGui::SetCursorPosX(float x)
6226 {
6227 ImGuiWindow* window = GetCurrentWindow();
6228 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
6229 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
6230 }
6231
SetCursorPosY(float y)6232 void ImGui::SetCursorPosY(float y)
6233 {
6234 ImGuiWindow* window = GetCurrentWindow();
6235 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
6236 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
6237 }
6238
GetCursorStartPos()6239 ImVec2 ImGui::GetCursorStartPos()
6240 {
6241 ImGuiWindow* window = GetCurrentWindowRead();
6242 return window->DC.CursorStartPos - window->Pos;
6243 }
6244
GetCursorScreenPos()6245 ImVec2 ImGui::GetCursorScreenPos()
6246 {
6247 ImGuiWindow* window = GetCurrentWindowRead();
6248 return window->DC.CursorPos;
6249 }
6250
SetCursorScreenPos(const ImVec2 & pos)6251 void ImGui::SetCursorScreenPos(const ImVec2& pos)
6252 {
6253 ImGuiWindow* window = GetCurrentWindow();
6254 window->DC.CursorPos = pos;
6255 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
6256 }
6257
GetScrollX()6258 float ImGui::GetScrollX()
6259 {
6260 return GImGui->CurrentWindow->Scroll.x;
6261 }
6262
GetScrollY()6263 float ImGui::GetScrollY()
6264 {
6265 return GImGui->CurrentWindow->Scroll.y;
6266 }
6267
GetScrollMaxX()6268 float ImGui::GetScrollMaxX()
6269 {
6270 return GetWindowScrollMaxX(GImGui->CurrentWindow);
6271 }
6272
GetScrollMaxY()6273 float ImGui::GetScrollMaxY()
6274 {
6275 return GetWindowScrollMaxY(GImGui->CurrentWindow);
6276 }
6277
SetScrollX(float scroll_x)6278 void ImGui::SetScrollX(float scroll_x)
6279 {
6280 ImGuiWindow* window = GetCurrentWindow();
6281 window->ScrollTarget.x = scroll_x;
6282 window->ScrollTargetCenterRatio.x = 0.0f;
6283 }
6284
SetScrollY(float scroll_y)6285 void ImGui::SetScrollY(float scroll_y)
6286 {
6287 ImGuiWindow* window = GetCurrentWindow();
6288 window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
6289 window->ScrollTargetCenterRatio.y = 0.0f;
6290 }
6291
SetScrollFromPosY(float local_y,float center_y_ratio)6292 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
6293 {
6294 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
6295 ImGuiWindow* window = GetCurrentWindow();
6296 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
6297 window->ScrollTarget.y = (float)(int)(local_y + window->Scroll.y);
6298 window->ScrollTargetCenterRatio.y = center_y_ratio;
6299 }
6300
6301 // 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)6302 void ImGui::SetScrollHereY(float center_y_ratio)
6303 {
6304 ImGuiWindow* window = GetCurrentWindow();
6305 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
6306 target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line.
6307 SetScrollFromPosY(target_y, center_y_ratio);
6308 }
6309
ActivateItem(ImGuiID id)6310 void ImGui::ActivateItem(ImGuiID id)
6311 {
6312 ImGuiContext& g = *GImGui;
6313 g.NavNextActivateId = id;
6314 }
6315
SetKeyboardFocusHere(int offset)6316 void ImGui::SetKeyboardFocusHere(int offset)
6317 {
6318 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6319 ImGuiWindow* window = GetCurrentWindow();
6320 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6321 window->FocusIdxTabRequestNext = INT_MAX;
6322 }
6323
SetItemDefaultFocus()6324 void ImGui::SetItemDefaultFocus()
6325 {
6326 ImGuiContext& g = *GImGui;
6327 ImGuiWindow* window = g.CurrentWindow;
6328 if (!window->Appearing)
6329 return;
6330 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6331 {
6332 g.NavInitRequest = false;
6333 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6334 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6335 NavUpdateAnyRequestFlag();
6336 if (!IsItemVisible())
6337 SetScrollHereY();
6338 }
6339 }
6340
SetStateStorage(ImGuiStorage * tree)6341 void ImGui::SetStateStorage(ImGuiStorage* tree)
6342 {
6343 ImGuiWindow* window = GetCurrentWindow();
6344 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6345 }
6346
GetStateStorage()6347 ImGuiStorage* ImGui::GetStateStorage()
6348 {
6349 ImGuiWindow* window = GetCurrentWindowRead();
6350 return window->DC.StateStorage;
6351 }
6352
PushID(const char * str_id)6353 void ImGui::PushID(const char* str_id)
6354 {
6355 ImGuiWindow* window = GetCurrentWindowRead();
6356 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6357 }
6358
PushID(const char * str_id_begin,const char * str_id_end)6359 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6360 {
6361 ImGuiWindow* window = GetCurrentWindowRead();
6362 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6363 }
6364
PushID(const void * ptr_id)6365 void ImGui::PushID(const void* ptr_id)
6366 {
6367 ImGuiWindow* window = GetCurrentWindowRead();
6368 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6369 }
6370
PushID(int int_id)6371 void ImGui::PushID(int int_id)
6372 {
6373 const void* ptr_id = (void*)(intptr_t)int_id;
6374 ImGuiWindow* window = GetCurrentWindowRead();
6375 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6376 }
6377
PopID()6378 void ImGui::PopID()
6379 {
6380 ImGuiWindow* window = GetCurrentWindowRead();
6381 window->IDStack.pop_back();
6382 }
6383
GetID(const char * str_id)6384 ImGuiID ImGui::GetID(const char* str_id)
6385 {
6386 return GImGui->CurrentWindow->GetID(str_id);
6387 }
6388
GetID(const char * str_id_begin,const char * str_id_end)6389 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6390 {
6391 return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6392 }
6393
GetID(const void * ptr_id)6394 ImGuiID ImGui::GetID(const void* ptr_id)
6395 {
6396 return GImGui->CurrentWindow->GetID(ptr_id);
6397 }
6398
IsRectVisible(const ImVec2 & size)6399 bool ImGui::IsRectVisible(const ImVec2& size)
6400 {
6401 ImGuiWindow* window = GetCurrentWindowRead();
6402 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6403 }
6404
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6405 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6406 {
6407 ImGuiWindow* window = GetCurrentWindowRead();
6408 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6409 }
6410
6411 // 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()6412 void ImGui::BeginGroup()
6413 {
6414 ImGuiContext& g = *GImGui;
6415 ImGuiWindow* window = GetCurrentWindow();
6416
6417 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6418 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6419 group_data.BackupCursorPos = window->DC.CursorPos;
6420 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6421 group_data.BackupIndent = window->DC.Indent;
6422 group_data.BackupGroupOffset = window->DC.GroupOffset;
6423 group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6424 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6425 group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6426 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6427 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6428 group_data.AdvanceCursor = true;
6429
6430 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6431 window->DC.Indent = window->DC.GroupOffset;
6432 window->DC.CursorMaxPos = window->DC.CursorPos;
6433 window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6434 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6435 }
6436
EndGroup()6437 void ImGui::EndGroup()
6438 {
6439 ImGuiContext& g = *GImGui;
6440 ImGuiWindow* window = GetCurrentWindow();
6441 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
6442
6443 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6444
6445 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6446 group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6447
6448 window->DC.CursorPos = group_data.BackupCursorPos;
6449 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6450 window->DC.Indent = group_data.BackupIndent;
6451 window->DC.GroupOffset = group_data.BackupGroupOffset;
6452 window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6453 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6454 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6455
6456 if (group_data.AdvanceCursor)
6457 {
6458 window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
6459 ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
6460 ItemAdd(group_bb, 0);
6461 }
6462
6463 // 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.
6464 // 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.
6465 // (and if you grep for LastItemId you'll notice it is only used in that context.
6466 if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6467 window->DC.LastItemId = g.ActiveId;
6468 else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6469 window->DC.LastItemId = g.ActiveIdPreviousFrame;
6470 window->DC.LastItemRect = group_bb;
6471
6472 window->DC.GroupStack.pop_back();
6473
6474 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
6475 }
6476
6477 // Gets back to previous line and continue with horizontal layout
6478 // pos_x == 0 : follow right after previous item
6479 // pos_x != 0 : align to specified x position (relative to window/group left)
6480 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
6481 // spacing_w >= 0 : enforce spacing amount
SameLine(float pos_x,float spacing_w)6482 void ImGui::SameLine(float pos_x, float spacing_w)
6483 {
6484 ImGuiWindow* window = GetCurrentWindow();
6485 if (window->SkipItems)
6486 return;
6487
6488 ImGuiContext& g = *GImGui;
6489 if (pos_x != 0.0f)
6490 {
6491 if (spacing_w < 0.0f) spacing_w = 0.0f;
6492 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6493 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6494 }
6495 else
6496 {
6497 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6498 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6499 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6500 }
6501 window->DC.CurrentLineSize = window->DC.PrevLineSize;
6502 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6503 }
6504
Indent(float indent_w)6505 void ImGui::Indent(float indent_w)
6506 {
6507 ImGuiContext& g = *GImGui;
6508 ImGuiWindow* window = GetCurrentWindow();
6509 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6510 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6511 }
6512
Unindent(float indent_w)6513 void ImGui::Unindent(float indent_w)
6514 {
6515 ImGuiContext& g = *GImGui;
6516 ImGuiWindow* window = GetCurrentWindow();
6517 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6518 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6519 }
6520
6521 //-----------------------------------------------------------------------------
6522 // [SECTION] TOOLTIPS
6523 //-----------------------------------------------------------------------------
6524
BeginTooltip()6525 void ImGui::BeginTooltip()
6526 {
6527 ImGuiContext& g = *GImGui;
6528 if (g.DragDropWithinSourceOrTarget)
6529 {
6530 // 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)
6531 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6532 // 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.
6533 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6534 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6535 SetNextWindowPos(tooltip_pos);
6536 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6537 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6538 BeginTooltipEx(0, true);
6539 }
6540 else
6541 {
6542 BeginTooltipEx(0, false);
6543 }
6544 }
6545
6546 // 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)6547 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6548 {
6549 ImGuiContext& g = *GImGui;
6550 char window_name[16];
6551 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6552 if (override_previous_tooltip)
6553 if (ImGuiWindow* window = FindWindowByName(window_name))
6554 if (window->Active)
6555 {
6556 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6557 window->Hidden = true;
6558 window->HiddenFramesRegular = 1;
6559 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6560 }
6561 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize;
6562 Begin(window_name, NULL, flags | extra_flags);
6563 }
6564
EndTooltip()6565 void ImGui::EndTooltip()
6566 {
6567 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
6568 End();
6569 }
6570
SetTooltipV(const char * fmt,va_list args)6571 void ImGui::SetTooltipV(const char* fmt, va_list args)
6572 {
6573 ImGuiContext& g = *GImGui;
6574 if (g.DragDropWithinSourceOrTarget)
6575 BeginTooltip();
6576 else
6577 BeginTooltipEx(0, true);
6578 TextV(fmt, args);
6579 EndTooltip();
6580 }
6581
SetTooltip(const char * fmt,...)6582 void ImGui::SetTooltip(const char* fmt, ...)
6583 {
6584 va_list args;
6585 va_start(args, fmt);
6586 SetTooltipV(fmt, args);
6587 va_end(args);
6588 }
6589
6590 //-----------------------------------------------------------------------------
6591 // [SECTION] POPUPS
6592 //-----------------------------------------------------------------------------
6593
IsPopupOpen(ImGuiID id)6594 bool ImGui::IsPopupOpen(ImGuiID id)
6595 {
6596 ImGuiContext& g = *GImGui;
6597 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
6598 }
6599
IsPopupOpen(const char * str_id)6600 bool ImGui::IsPopupOpen(const char* str_id)
6601 {
6602 ImGuiContext& g = *GImGui;
6603 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6604 }
6605
GetFrontMostPopupModal()6606 ImGuiWindow* ImGui::GetFrontMostPopupModal()
6607 {
6608 ImGuiContext& g = *GImGui;
6609 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6610 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6611 if (popup->Flags & ImGuiWindowFlags_Modal)
6612 return popup;
6613 return NULL;
6614 }
6615
OpenPopup(const char * str_id)6616 void ImGui::OpenPopup(const char* str_id)
6617 {
6618 ImGuiContext& g = *GImGui;
6619 OpenPopupEx(g.CurrentWindow->GetID(str_id));
6620 }
6621
6622 // Mark popup as open (toggle toward open state).
6623 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6624 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6625 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)6626 void ImGui::OpenPopupEx(ImGuiID id)
6627 {
6628 ImGuiContext& g = *GImGui;
6629 ImGuiWindow* parent_window = g.CurrentWindow;
6630 int current_stack_size = g.BeginPopupStack.Size;
6631 ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6632 popup_ref.PopupId = id;
6633 popup_ref.Window = NULL;
6634 popup_ref.ParentWindow = parent_window;
6635 popup_ref.OpenFrameCount = g.FrameCount;
6636 popup_ref.OpenParentId = parent_window->IDStack.back();
6637 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6638 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
6639
6640 //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6641 if (g.OpenPopupStack.Size < current_stack_size + 1)
6642 {
6643 g.OpenPopupStack.push_back(popup_ref);
6644 }
6645 else
6646 {
6647 // 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
6648 // 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
6649 // 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.
6650 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6651 {
6652 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6653 }
6654 else
6655 {
6656 // Close child popups if any, then flag popup for open/reopen
6657 g.OpenPopupStack.resize(current_stack_size + 1);
6658 g.OpenPopupStack[current_stack_size] = popup_ref;
6659 }
6660
6661 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6662 // This is equivalent to what ClosePopupToLevel() does.
6663 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6664 // FocusWindow(parent_window);
6665 }
6666 }
6667
OpenPopupOnItemClick(const char * str_id,int mouse_button)6668 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6669 {
6670 ImGuiWindow* window = GImGui->CurrentWindow;
6671 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6672 {
6673 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!
6674 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6675 OpenPopupEx(id);
6676 return true;
6677 }
6678 return false;
6679 }
6680
ClosePopupsOverWindow(ImGuiWindow * ref_window)6681 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6682 {
6683 ImGuiContext& g = *GImGui;
6684 if (g.OpenPopupStack.empty())
6685 return;
6686
6687 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6688 // Don't close our own child popup windows.
6689 int popup_count_to_keep = 0;
6690 if (ref_window)
6691 {
6692 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
6693 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
6694 {
6695 ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep];
6696 if (!popup.Window)
6697 continue;
6698 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6699 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6700 continue;
6701
6702 // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6703 bool popup_or_descendent_has_focus = false;
6704 for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++)
6705 if (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow)
6706 popup_or_descendent_has_focus = true;
6707 if (!popup_or_descendent_has_focus)
6708 break;
6709 }
6710 }
6711 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
6712 {
6713 //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep);
6714 ClosePopupToLevel(popup_count_to_keep, false);
6715 }
6716 }
6717
ClosePopupToLevel(int remaining,bool apply_focus_to_window_under)6718 void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under)
6719 {
6720 IM_ASSERT(remaining >= 0);
6721 ImGuiContext& g = *GImGui;
6722 ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6723 g.OpenPopupStack.resize(remaining);
6724
6725 // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely.
6726 // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing.
6727 // The current code will set focus to the parent of the popup window which is incorrect.
6728 // It rarely manifested until now because UpdateMouseMovingWindow() would call FocusWindow() again on the clicked window,
6729 // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again.
6730 // However if the clicked window has the _NoMove flag set we would be left with B focused.
6731 // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway,
6732 // but we should inspect and fix this properly.
6733 if (apply_focus_to_window_under)
6734 {
6735 if (g.NavLayer == 0)
6736 focus_window = NavRestoreLastChildNavWindow(focus_window);
6737 FocusWindow(focus_window);
6738 }
6739 }
6740
6741 // Close the popup we have begin-ed into.
CloseCurrentPopup()6742 void ImGui::CloseCurrentPopup()
6743 {
6744 ImGuiContext& g = *GImGui;
6745 int popup_idx = g.BeginPopupStack.Size - 1;
6746 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6747 return;
6748 while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
6749 popup_idx--;
6750 ClosePopupToLevel(popup_idx, true);
6751
6752 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
6753 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
6754 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
6755 if (ImGuiWindow* window = g.NavWindow)
6756 window->DC.NavHideHighlightOneFrame = true;
6757 }
6758
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)6759 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6760 {
6761 ImGuiContext& g = *GImGui;
6762 if (!IsPopupOpen(id))
6763 {
6764 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6765 return false;
6766 }
6767
6768 char name[20];
6769 if (extra_flags & ImGuiWindowFlags_ChildMenu)
6770 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
6771 else
6772 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6773
6774 bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6775 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6776 EndPopup();
6777
6778 return is_open;
6779 }
6780
BeginPopup(const char * str_id,ImGuiWindowFlags flags)6781 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6782 {
6783 ImGuiContext& g = *GImGui;
6784 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
6785 {
6786 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6787 return false;
6788 }
6789 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
6790 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags);
6791 }
6792
6793 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
6794 // Note that popup visibility status is owned by 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)6795 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6796 {
6797 ImGuiContext& g = *GImGui;
6798 ImGuiWindow* window = g.CurrentWindow;
6799 const ImGuiID id = window->GetID(name);
6800 if (!IsPopupOpen(id))
6801 {
6802 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6803 return false;
6804 }
6805
6806 // Center modal windows by default
6807 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6808 if (g.NextWindowData.PosCond == 0)
6809 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6810
6811 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings;
6812 const bool is_open = Begin(name, p_open, flags);
6813 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6814 {
6815 EndPopup();
6816 if (is_open)
6817 ClosePopupToLevel(g.BeginPopupStack.Size, true);
6818 return false;
6819 }
6820 return is_open;
6821 }
6822
EndPopup()6823 void ImGui::EndPopup()
6824 {
6825 ImGuiContext& g = *GImGui; (void)g;
6826 IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
6827 IM_ASSERT(g.BeginPopupStack.Size > 0);
6828
6829 // Make all menus and popups wrap around for now, may need to expose that policy.
6830 NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6831
6832 End();
6833 }
6834
6835 // This is a helper to handle the simplest case of associating one named popup to one given widget.
6836 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6837 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)6838 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6839 {
6840 ImGuiWindow* window = GImGui->CurrentWindow;
6841 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!
6842 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6843 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6844 OpenPopupEx(id);
6845 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6846 }
6847
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)6848 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6849 {
6850 if (!str_id)
6851 str_id = "window_context";
6852 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6853 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6854 if (also_over_items || !IsAnyItemHovered())
6855 OpenPopupEx(id);
6856 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6857 }
6858
BeginPopupContextVoid(const char * str_id,int mouse_button)6859 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
6860 {
6861 if (!str_id)
6862 str_id = "void_context";
6863 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6864 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
6865 OpenPopupEx(id);
6866 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6867 }
6868
GetWindowAllowedExtentRect(ImGuiWindow *)6869 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
6870 {
6871 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
6872 ImRect r_screen = GetViewportRect();
6873 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
6874 return r_screen;
6875 }
6876
6877 // 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.)
6878 // 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)6879 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
6880 {
6881 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
6882 //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
6883 //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
6884
6885 // Combo Box policy (we want a connecting edge)
6886 if (policy == ImGuiPopupPositionPolicy_ComboBox)
6887 {
6888 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
6889 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6890 {
6891 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6892 if (n != -1 && dir == *last_dir) // Already tried this direction?
6893 continue;
6894 ImVec2 pos;
6895 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
6896 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
6897 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
6898 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
6899 if (!r_outer.Contains(ImRect(pos, pos + size)))
6900 continue;
6901 *last_dir = dir;
6902 return pos;
6903 }
6904 }
6905
6906 // Default popup policy
6907 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
6908 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6909 {
6910 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6911 if (n != -1 && dir == *last_dir) // Already tried this direction?
6912 continue;
6913 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);
6914 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);
6915 if (avail_w < size.x || avail_h < size.y)
6916 continue;
6917 ImVec2 pos;
6918 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
6919 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
6920 *last_dir = dir;
6921 return pos;
6922 }
6923
6924 // Fallback, try to keep within display
6925 *last_dir = ImGuiDir_None;
6926 ImVec2 pos = ref_pos;
6927 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
6928 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
6929 return pos;
6930 }
6931
FindBestWindowPosForPopup(ImGuiWindow * window)6932 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
6933 {
6934 ImGuiContext& g = *GImGui;
6935
6936 ImRect r_outer = GetWindowAllowedExtentRect(window);
6937 if (window->Flags & ImGuiWindowFlags_ChildMenu)
6938 {
6939 // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds.
6940 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
6941 IM_ASSERT(g.CurrentWindow == window);
6942 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
6943 float horizontal_overlap = g.Style.ItemSpacing.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).
6944 ImRect r_avoid;
6945 if (parent_window->DC.MenuBarAppending)
6946 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
6947 else
6948 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);
6949 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6950 }
6951 if (window->Flags & ImGuiWindowFlags_Popup)
6952 {
6953 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
6954 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6955 }
6956 if (window->Flags & ImGuiWindowFlags_Tooltip)
6957 {
6958 // Position tooltip (always follows mouse)
6959 float sc = g.Style.MouseCursorScale;
6960 ImVec2 ref_pos = NavCalcPreferredRefPos();
6961 ImRect r_avoid;
6962 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
6963 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6964 else
6965 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.
6966 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6967 if (window->AutoPosLastDirection == ImGuiDir_None)
6968 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.
6969 return pos;
6970 }
6971 IM_ASSERT(0);
6972 return window->Pos;
6973 }
6974
6975 //-----------------------------------------------------------------------------
6976 // [SECTION] VIEWPORTS, PLATFORM WINDOWS
6977 //-----------------------------------------------------------------------------
6978
6979 // (this section is filled in the 'viewport' and 'docking' branches)
6980
6981 //-----------------------------------------------------------------------------
6982 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
6983 //-----------------------------------------------------------------------------
6984
ImGetDirQuadrantFromDelta(float dx,float dy)6985 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
6986 {
6987 if (ImFabs(dx) > ImFabs(dy))
6988 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
6989 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
6990 }
6991
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)6992 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
6993 {
6994 if (a1 < b0)
6995 return a1 - b0;
6996 if (b1 < a0)
6997 return a0 - b1;
6998 return 0.0f;
6999 }
7000
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)7001 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
7002 {
7003 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
7004 {
7005 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
7006 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
7007 }
7008 else
7009 {
7010 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
7011 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
7012 }
7013 }
7014
7015 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)7016 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
7017 {
7018 ImGuiContext& g = *GImGui;
7019 ImGuiWindow* window = g.CurrentWindow;
7020 if (g.NavLayer != window->DC.NavLayerCurrent)
7021 return false;
7022
7023 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)
7024 g.NavScoringCount++;
7025
7026 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
7027 if (window->ParentWindow == g.NavWindow)
7028 {
7029 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
7030 if (!window->ClipRect.Contains(cand))
7031 return false;
7032 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
7033 }
7034
7035 // 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)
7036 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
7037 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
7038
7039 // Compute distance between boxes
7040 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
7041 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
7042 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
7043 if (dby != 0.0f && dbx != 0.0f)
7044 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
7045 float dist_box = ImFabs(dbx) + ImFabs(dby);
7046
7047 // 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)
7048 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
7049 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
7050 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
7051
7052 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
7053 ImGuiDir quadrant;
7054 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
7055 if (dbx != 0.0f || dby != 0.0f)
7056 {
7057 // For non-overlapping boxes, use distance between boxes
7058 dax = dbx;
7059 day = dby;
7060 dist_axial = dist_box;
7061 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
7062 }
7063 else if (dcx != 0.0f || dcy != 0.0f)
7064 {
7065 // For overlapping boxes with different centers, use distance between centers
7066 dax = dcx;
7067 day = dcy;
7068 dist_axial = dist_center;
7069 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
7070 }
7071 else
7072 {
7073 // 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)
7074 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
7075 }
7076
7077 #if IMGUI_DEBUG_NAV_SCORING
7078 char buf[128];
7079 if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
7080 {
7081 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]);
7082 ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7083 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
7084 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
7085 draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
7086 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
7087 }
7088 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
7089 {
7090 if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
7091 if (quadrant == g.NavMoveDir)
7092 {
7093 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
7094 ImDrawList* draw_list = ImGui::GetOverlayDrawList(window);
7095 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
7096 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
7097 }
7098 }
7099 #endif
7100
7101 // Is it in the quadrant we're interesting in moving to?
7102 bool new_best = false;
7103 if (quadrant == g.NavMoveDir)
7104 {
7105 // Does it beat the current best candidate?
7106 if (dist_box < result->DistBox)
7107 {
7108 result->DistBox = dist_box;
7109 result->DistCenter = dist_center;
7110 return true;
7111 }
7112 if (dist_box == result->DistBox)
7113 {
7114 // Try using distance between center points to break ties
7115 if (dist_center < result->DistCenter)
7116 {
7117 result->DistCenter = dist_center;
7118 new_best = true;
7119 }
7120 else if (dist_center == result->DistCenter)
7121 {
7122 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
7123 // (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),
7124 // 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.
7125 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
7126 new_best = true;
7127 }
7128 }
7129 }
7130
7131 // 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
7132 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
7133 // 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.
7134 // 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.
7135 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
7136 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
7137 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
7138 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))
7139 {
7140 result->DistAxial = dist_axial;
7141 new_best = true;
7142 }
7143
7144 return new_best;
7145 }
7146
7147 // 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)7148 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
7149 {
7150 ImGuiContext& g = *GImGui;
7151 //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.
7152 // return;
7153
7154 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
7155 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
7156
7157 // Process Init Request
7158 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
7159 {
7160 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
7161 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
7162 {
7163 g.NavInitResultId = id;
7164 g.NavInitResultRectRel = nav_bb_rel;
7165 }
7166 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
7167 {
7168 g.NavInitRequest = false; // Found a match, clear request
7169 NavUpdateAnyRequestFlag();
7170 }
7171 }
7172
7173 // Process Move Request (scoring for navigation)
7174 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
7175 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
7176 {
7177 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7178 #if IMGUI_DEBUG_NAV_SCORING
7179 // [DEBUG] Score all items in NavWindow at all times
7180 if (!g.NavMoveRequest)
7181 g.NavMoveDir = g.NavMoveDirLast;
7182 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
7183 #else
7184 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
7185 #endif
7186 if (new_best)
7187 {
7188 result->ID = id;
7189 result->Window = window;
7190 result->RectRel = nav_bb_rel;
7191 }
7192
7193 const float VISIBLE_RATIO = 0.70f;
7194 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
7195 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)
7196 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
7197 {
7198 result = &g.NavMoveResultLocalVisibleSet;
7199 result->ID = id;
7200 result->Window = window;
7201 result->RectRel = nav_bb_rel;
7202 }
7203 }
7204
7205 // Update window-relative bounding box of navigated item
7206 if (g.NavId == id)
7207 {
7208 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
7209 g.NavLayer = window->DC.NavLayerCurrent;
7210 g.NavIdIsAlive = true;
7211 g.NavIdTabCounter = window->FocusIdxTabCounter;
7212 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
7213 }
7214 }
7215
NavMoveRequestButNoResultYet()7216 bool ImGui::NavMoveRequestButNoResultYet()
7217 {
7218 ImGuiContext& g = *GImGui;
7219 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
7220 }
7221
NavMoveRequestCancel()7222 void ImGui::NavMoveRequestCancel()
7223 {
7224 ImGuiContext& g = *GImGui;
7225 g.NavMoveRequest = false;
7226 NavUpdateAnyRequestFlag();
7227 }
7228
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)7229 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
7230 {
7231 ImGuiContext& g = *GImGui;
7232 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
7233 ImGui::NavMoveRequestCancel();
7234 g.NavMoveDir = move_dir;
7235 g.NavMoveClipDir = clip_dir;
7236 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
7237 g.NavMoveRequestFlags = move_flags;
7238 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
7239 }
7240
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)7241 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
7242 {
7243 ImGuiContext& g = *GImGui;
7244 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
7245 return;
7246 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
7247 ImRect bb_rel = window->NavRectRel[0];
7248
7249 ImGuiDir clip_dir = g.NavMoveDir;
7250 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7251 {
7252 bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
7253 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
7254 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7255 }
7256 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
7257 {
7258 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
7259 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
7260 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7261 }
7262 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7263 {
7264 bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
7265 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
7266 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7267 }
7268 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
7269 {
7270 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
7271 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
7272 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
7273 }
7274 }
7275
NavSaveLastChildNavWindow(ImGuiWindow * nav_window)7276 static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
7277 {
7278 ImGuiWindow* parent_window = nav_window;
7279 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7280 parent_window = parent_window->ParentWindow;
7281 if (parent_window && parent_window != nav_window)
7282 parent_window->NavLastChildNavWindow = nav_window;
7283 }
7284
7285 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)7286 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
7287 {
7288 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
7289 }
7290
NavRestoreLayer(ImGuiNavLayer layer)7291 static void NavRestoreLayer(ImGuiNavLayer layer)
7292 {
7293 ImGuiContext& g = *GImGui;
7294 g.NavLayer = layer;
7295 if (layer == 0)
7296 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
7297 if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
7298 ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
7299 else
7300 ImGui::NavInitWindow(g.NavWindow, true);
7301 }
7302
NavUpdateAnyRequestFlag()7303 static inline void ImGui::NavUpdateAnyRequestFlag()
7304 {
7305 ImGuiContext& g = *GImGui;
7306 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
7307 if (g.NavAnyRequest)
7308 IM_ASSERT(g.NavWindow != NULL);
7309 }
7310
7311 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)7312 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
7313 {
7314 ImGuiContext& g = *GImGui;
7315 IM_ASSERT(window == g.NavWindow);
7316 bool init_for_nav = false;
7317 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
7318 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
7319 init_for_nav = true;
7320 if (init_for_nav)
7321 {
7322 SetNavID(0, g.NavLayer);
7323 g.NavInitRequest = true;
7324 g.NavInitRequestFromMove = false;
7325 g.NavInitResultId = 0;
7326 g.NavInitResultRectRel = ImRect();
7327 NavUpdateAnyRequestFlag();
7328 }
7329 else
7330 {
7331 g.NavId = window->NavLastIds[0];
7332 }
7333 }
7334
NavCalcPreferredRefPos()7335 static ImVec2 ImGui::NavCalcPreferredRefPos()
7336 {
7337 ImGuiContext& g = *GImGui;
7338 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7339 {
7340 // Mouse (we need a fallback in case the mouse becomes invalid after being used)
7341 if (IsMousePosValid(&g.IO.MousePos))
7342 return g.IO.MousePos;
7343 return g.LastValidMousePos;
7344 }
7345 else
7346 {
7347 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item.
7348 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7349 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()));
7350 ImRect visible_rect = GetViewportRect();
7351 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.
7352 }
7353 }
7354
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)7355 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7356 {
7357 ImGuiContext& g = *GImGui;
7358 if (mode == ImGuiInputReadMode_Down)
7359 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
7360
7361 const float t = g.IO.NavInputsDownDuration[n];
7362 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
7363 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7364 if (t < 0.0f)
7365 return 0.0f;
7366 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
7367 return (t == 0.0f) ? 1.0f : 0.0f;
7368 if (mode == ImGuiInputReadMode_Repeat)
7369 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7370 if (mode == ImGuiInputReadMode_RepeatSlow)
7371 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7372 if (mode == ImGuiInputReadMode_RepeatFast)
7373 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7374 return 0.0f;
7375 }
7376
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)7377 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7378 {
7379 ImVec2 delta(0.0f, 0.0f);
7380 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7381 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
7382 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7383 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
7384 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7385 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7386 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7387 delta *= slow_factor;
7388 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7389 delta *= fast_factor;
7390 return delta;
7391 }
7392
7393 // Scroll to keep newly navigated item fully into view
7394 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,const ImRect & item_rect)7395 static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7396 {
7397 ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7398 //GetOverlayDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7399 if (window_rect.Contains(item_rect))
7400 return;
7401
7402 ImGuiContext& g = *GImGui;
7403 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7404 {
7405 window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7406 window->ScrollTargetCenterRatio.x = 0.0f;
7407 }
7408 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7409 {
7410 window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7411 window->ScrollTargetCenterRatio.x = 1.0f;
7412 }
7413 if (item_rect.Min.y < window_rect.Min.y)
7414 {
7415 window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7416 window->ScrollTargetCenterRatio.y = 0.0f;
7417 }
7418 else if (item_rect.Max.y >= window_rect.Max.y)
7419 {
7420 window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7421 window->ScrollTargetCenterRatio.y = 1.0f;
7422 }
7423 }
7424
NavUpdate()7425 static void ImGui::NavUpdate()
7426 {
7427 ImGuiContext& g = *GImGui;
7428 g.IO.WantSetMousePos = false;
7429 #if 0
7430 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);
7431 #endif
7432
7433 // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7434 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7435 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7436 if (nav_gamepad_active)
7437 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)
7438 g.NavInputSource = ImGuiInputSource_NavGamepad;
7439
7440 // Update Keyboard->Nav inputs mapping
7441 if (nav_keyboard_active)
7442 {
7443 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7444 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
7445 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
7446 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
7447 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7448 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7449 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
7450 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7451 if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7452 if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7453 if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
7454 #undef NAV_MAP_KEY
7455 }
7456 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7457 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7458 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;
7459
7460 // Process navigation init request (select first/default focus)
7461 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7462 {
7463 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7464 IM_ASSERT(g.NavWindow);
7465 if (g.NavInitRequestFromMove)
7466 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7467 else
7468 SetNavID(g.NavInitResultId, g.NavLayer);
7469 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7470 }
7471 g.NavInitRequest = false;
7472 g.NavInitRequestFromMove = false;
7473 g.NavInitResultId = 0;
7474 g.NavJustMovedToId = 0;
7475
7476 // Process navigation move request
7477 if (g.NavMoveRequest)
7478 NavUpdateMoveResult();
7479
7480 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7481 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7482 {
7483 IM_ASSERT(g.NavMoveRequest);
7484 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7485 g.NavDisableHighlight = false;
7486 g.NavMoveRequestForward = ImGuiNavForward_None;
7487 }
7488
7489 // Apply application mouse position movement, after we had a chance to process move request result.
7490 if (g.NavMousePosDirty && g.NavIdIsAlive)
7491 {
7492 // Set mouse position given our knowledge of the navigated item position from last frame
7493 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7494 {
7495 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7496 {
7497 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7498 g.IO.WantSetMousePos = true;
7499 }
7500 }
7501 g.NavMousePosDirty = false;
7502 }
7503 g.NavIdIsAlive = false;
7504 g.NavJustTabbedId = 0;
7505 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7506
7507 // 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
7508 if (g.NavWindow)
7509 NavSaveLastChildNavWindow(g.NavWindow);
7510 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7511 g.NavWindow->NavLastChildNavWindow = NULL;
7512
7513 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7514 NavUpdateWindowing();
7515
7516 // Set output flags for user application
7517 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7518 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
7519
7520 // Process NavCancel input (to close a popup, get back to parent, clear focus)
7521 if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7522 {
7523 if (g.ActiveId != 0)
7524 {
7525 ClearActiveID();
7526 }
7527 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7528 {
7529 // Exit child window
7530 ImGuiWindow* child_window = g.NavWindow;
7531 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7532 IM_ASSERT(child_window->ChildId != 0);
7533 FocusWindow(parent_window);
7534 SetNavID(child_window->ChildId, 0);
7535 g.NavIdIsAlive = false;
7536 if (g.NavDisableMouseHover)
7537 g.NavMousePosDirty = true;
7538 }
7539 else if (g.OpenPopupStack.Size > 0)
7540 {
7541 // Close open popup/menu
7542 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7543 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
7544 }
7545 else if (g.NavLayer != 0)
7546 {
7547 // Leave the "menu" layer
7548 NavRestoreLayer(ImGuiNavLayer_Main);
7549 }
7550 else
7551 {
7552 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7553 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7554 g.NavWindow->NavLastIds[0] = 0;
7555 g.NavId = 0;
7556 }
7557 }
7558
7559 // Process manual activation request
7560 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7561 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7562 {
7563 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7564 bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7565 if (g.ActiveId == 0 && activate_pressed)
7566 g.NavActivateId = g.NavId;
7567 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7568 g.NavActivateDownId = g.NavId;
7569 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7570 g.NavActivatePressedId = g.NavId;
7571 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7572 g.NavInputId = g.NavId;
7573 }
7574 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7575 g.NavDisableHighlight = true;
7576 if (g.NavActivateId != 0)
7577 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7578 g.NavMoveRequest = false;
7579
7580 // Process programmatic activation request
7581 if (g.NavNextActivateId != 0)
7582 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7583 g.NavNextActivateId = 0;
7584
7585 // Initiate directional inputs request
7586 const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7587 if (g.NavMoveRequestForward == ImGuiNavForward_None)
7588 {
7589 g.NavMoveDir = ImGuiDir_None;
7590 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None;
7591 if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7592 {
7593 if ((allowed_dir_flags & (1<<ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7594 if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7595 if ((allowed_dir_flags & (1<<ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7596 if ((allowed_dir_flags & (1<<ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7597 }
7598 g.NavMoveClipDir = g.NavMoveDir;
7599 }
7600 else
7601 {
7602 // 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)
7603 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7604 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7605 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7606 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7607 }
7608
7609 // Update PageUp/PageDown scroll
7610 float nav_scoring_rect_offset_y = 0.0f;
7611 if (nav_keyboard_active)
7612 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7613
7614 // 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
7615 if (g.NavMoveDir != ImGuiDir_None)
7616 {
7617 g.NavMoveRequest = true;
7618 g.NavMoveDirLast = g.NavMoveDir;
7619 }
7620 if (g.NavMoveRequest && g.NavId == 0)
7621 {
7622 g.NavInitRequest = g.NavInitRequestFromMove = true;
7623 g.NavInitResultId = 0;
7624 g.NavDisableHighlight = false;
7625 }
7626 NavUpdateAnyRequestFlag();
7627
7628 // Scrolling
7629 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7630 {
7631 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7632 ImGuiWindow* window = g.NavWindow;
7633 const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
7634 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7635 {
7636 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7637 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7638 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7639 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7640 }
7641
7642 // *Normal* Manual scroll with NavScrollXXX keys
7643 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7644 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7645 if (scroll_dir.x != 0.0f && window->ScrollbarX)
7646 {
7647 SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7648 g.NavMoveFromClampedRefRect = true;
7649 }
7650 if (scroll_dir.y != 0.0f)
7651 {
7652 SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7653 g.NavMoveFromClampedRefRect = true;
7654 }
7655 }
7656
7657 // Reset search results
7658 g.NavMoveResultLocal.Clear();
7659 g.NavMoveResultLocalVisibleSet.Clear();
7660 g.NavMoveResultOther.Clear();
7661
7662 // 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
7663 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7664 {
7665 ImGuiWindow* window = g.NavWindow;
7666 ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7667 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7668 {
7669 float pad = window->CalcFontSize() * 0.5f;
7670 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
7671 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7672 g.NavId = 0;
7673 }
7674 g.NavMoveFromClampedRefRect = false;
7675 }
7676
7677 // 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)
7678 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7679 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7680 g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7681 g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7682 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7683 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().
7684 //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7685 g.NavScoringCount = 0;
7686 #if IMGUI_DEBUG_NAV_RECTS
7687 if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7688 if (g.NavWindow) { 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); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7689 #endif
7690 }
7691
7692 // Apply result from previous frame navigation directional move request
NavUpdateMoveResult()7693 static void ImGui::NavUpdateMoveResult()
7694 {
7695 ImGuiContext& g = *GImGui;
7696 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7697 {
7698 // 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)
7699 if (g.NavId != 0)
7700 {
7701 g.NavDisableHighlight = false;
7702 g.NavDisableMouseHover = true;
7703 }
7704 return;
7705 }
7706
7707 // Select which result to use
7708 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7709
7710 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7711 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7712 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7713 result = &g.NavMoveResultLocalVisibleSet;
7714
7715 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7716 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7717 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7718 result = &g.NavMoveResultOther;
7719 IM_ASSERT(g.NavWindow && result->Window);
7720
7721 // Scroll to keep newly navigated item fully into view.
7722 if (g.NavLayer == 0)
7723 {
7724 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7725 NavScrollToBringItemIntoView(result->Window, rect_abs);
7726
7727 // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7728 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7729 ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7730 result->RectRel.Translate(delta_scroll);
7731
7732 // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7733 if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7734 NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7735 }
7736
7737 ClearActiveID();
7738 g.NavWindow = result->Window;
7739 SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7740 g.NavJustMovedToId = result->ID;
7741 g.NavMoveFromClampedRefRect = false;
7742 }
7743
NavUpdatePageUpPageDown(int allowed_dir_flags)7744 static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7745 {
7746 ImGuiContext& g = *GImGui;
7747 if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7748 {
7749 ImGuiWindow* window = g.NavWindow;
7750 bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7751 bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7752 if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
7753 {
7754 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7755 {
7756 // Fallback manual-scroll when window has no navigable item
7757 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7758 SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7759 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7760 SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7761 }
7762 else
7763 {
7764 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7765 const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7766 float nav_scoring_rect_offset_y = 0.0f;
7767 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7768 {
7769 nav_scoring_rect_offset_y = -page_offset_y;
7770 g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7771 g.NavMoveClipDir = ImGuiDir_Up;
7772 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7773 }
7774 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7775 {
7776 nav_scoring_rect_offset_y = +page_offset_y;
7777 g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item)
7778 g.NavMoveClipDir = ImGuiDir_Down;
7779 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7780 }
7781 return nav_scoring_rect_offset_y;
7782 }
7783 }
7784 }
7785 return 0.0f;
7786 }
7787
FindWindowFocusIndex(ImGuiWindow * window)7788 static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7789 {
7790 ImGuiContext& g = *GImGui;
7791 for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--)
7792 if (g.WindowsFocusOrder[i] == window)
7793 return i;
7794 return -1;
7795 }
7796
FindWindowNavFocusable(int i_start,int i_stop,int dir)7797 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7798 {
7799 ImGuiContext& g = *GImGui;
7800 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
7801 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
7802 return g.WindowsFocusOrder[i];
7803 return NULL;
7804 }
7805
NavUpdateWindowingHighlightWindow(int focus_change_dir)7806 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7807 {
7808 ImGuiContext& g = *GImGui;
7809 IM_ASSERT(g.NavWindowingTarget);
7810 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7811 return;
7812
7813 const int i_current = FindWindowFocusIndex(g.NavWindowingTarget);
7814 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7815 if (!window_target)
7816 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
7817 if (window_target) // Don't reset windowing target if there's a single window in the list
7818 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7819 g.NavWindowingToggleLayer = false;
7820 }
7821
7822 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()7823 static void ImGui::NavUpdateWindowing()
7824 {
7825 ImGuiContext& g = *GImGui;
7826 ImGuiWindow* apply_focus_window = NULL;
7827 bool apply_toggle_layer = false;
7828
7829 ImGuiWindow* modal_window = GetFrontMostPopupModal();
7830 if (modal_window != NULL)
7831 {
7832 g.NavWindowingTarget = NULL;
7833 return;
7834 }
7835
7836 // Fade out
7837 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7838 {
7839 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7840 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7841 g.NavWindowingTargetAnim = NULL;
7842 }
7843
7844 // Start CTRL-TAB or Square+L/R window selection
7845 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7846 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7847 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7848 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
7849 {
7850 g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
7851 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
7852 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
7853 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
7854 }
7855
7856 // Gamepad update
7857 g.NavWindowingTimer += g.IO.DeltaTime;
7858 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
7859 {
7860 // 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
7861 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
7862
7863 // Select window to focus
7864 const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
7865 if (focus_change_dir != 0)
7866 {
7867 NavUpdateWindowingHighlightWindow(focus_change_dir);
7868 g.NavWindowingHighlightAlpha = 1.0f;
7869 }
7870
7871 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
7872 if (!IsNavInputDown(ImGuiNavInput_Menu))
7873 {
7874 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
7875 if (g.NavWindowingToggleLayer && g.NavWindow)
7876 apply_toggle_layer = true;
7877 else if (!g.NavWindowingToggleLayer)
7878 apply_focus_window = g.NavWindowingTarget;
7879 g.NavWindowingTarget = NULL;
7880 }
7881 }
7882
7883 // Keyboard: Focus
7884 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
7885 {
7886 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
7887 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
7888 if (IsKeyPressedMap(ImGuiKey_Tab, true))
7889 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
7890 if (!g.IO.KeyCtrl)
7891 apply_focus_window = g.NavWindowingTarget;
7892 }
7893
7894 // Keyboard: Press and Release ALT to toggle menu layer
7895 // 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
7896 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
7897 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
7898 apply_toggle_layer = true;
7899
7900 // Move window
7901 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
7902 {
7903 ImVec2 move_delta;
7904 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
7905 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
7906 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
7907 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
7908 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
7909 {
7910 const float NAV_MOVE_SPEED = 800.0f;
7911 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
7912 g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
7913 g.NavDisableMouseHover = true;
7914 MarkIniSettingsDirty(g.NavWindowingTarget);
7915 }
7916 }
7917
7918 // Apply final focus
7919 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
7920 {
7921 g.NavDisableHighlight = false;
7922 g.NavDisableMouseHover = true;
7923 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
7924 ClosePopupsOverWindow(apply_focus_window);
7925 FocusWindow(apply_focus_window);
7926 if (apply_focus_window->NavLastIds[0] == 0)
7927 NavInitWindow(apply_focus_window, false);
7928
7929 // If the window only has a menu layer, select it directly
7930 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu))
7931 g.NavLayer = ImGuiNavLayer_Menu;
7932 }
7933 if (apply_focus_window)
7934 g.NavWindowingTarget = NULL;
7935
7936 // Apply menu/layer toggle
7937 if (apply_toggle_layer && g.NavWindow)
7938 {
7939 // Move to parent menu if necessary
7940 ImGuiWindow* new_nav_window = g.NavWindow;
7941 while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0
7942 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
7943 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7944 new_nav_window = new_nav_window->ParentWindow;
7945 if (new_nav_window != g.NavWindow)
7946 {
7947 ImGuiWindow* old_nav_window = g.NavWindow;
7948 FocusWindow(new_nav_window);
7949 new_nav_window->NavLastChildNavWindow = old_nav_window;
7950 }
7951 g.NavDisableHighlight = false;
7952 g.NavDisableMouseHover = true;
7953 NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main);
7954 }
7955 }
7956
7957 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)7958 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
7959 {
7960 if (window->Flags & ImGuiWindowFlags_Popup)
7961 return "(Popup)";
7962 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
7963 return "(Main menu bar)";
7964 return "(Untitled)";
7965 }
7966
7967 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingList()7968 void ImGui::NavUpdateWindowingList()
7969 {
7970 ImGuiContext& g = *GImGui;
7971 IM_ASSERT(g.NavWindowingTarget != NULL);
7972
7973 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
7974 return;
7975
7976 if (g.NavWindowingList == NULL)
7977 g.NavWindowingList = FindWindowByName("###NavWindowingList");
7978 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
7979 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
7980 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
7981 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
7982 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
7983 {
7984 ImGuiWindow* window = g.WindowsFocusOrder[n];
7985 if (!IsWindowNavFocusable(window))
7986 continue;
7987 const char* label = window->Name;
7988 if (label == FindRenderedTextEnd(label))
7989 label = GetFallbackWindowNameForWindowingList(window);
7990 Selectable(label, g.NavWindowingTarget == window);
7991 }
7992 End();
7993 PopStyleVar();
7994 }
7995
7996 //-----------------------------------------------------------------------------
7997 // [SECTION] COLUMNS
7998 // In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
7999 //-----------------------------------------------------------------------------
8000
NextColumn()8001 void ImGui::NextColumn()
8002 {
8003 ImGuiWindow* window = GetCurrentWindow();
8004 if (window->SkipItems || window->DC.ColumnsSet == NULL)
8005 return;
8006
8007 ImGuiContext& g = *GImGui;
8008 PopItemWidth();
8009 PopClipRect();
8010
8011 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8012 columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8013 if (++columns->Current < columns->Count)
8014 {
8015 // Columns 1+ cancel out IndentX
8016 window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
8017 window->DrawList->ChannelsSetCurrent(columns->Current);
8018 }
8019 else
8020 {
8021 window->DC.ColumnsOffset.x = 0.0f;
8022 window->DrawList->ChannelsSetCurrent(0);
8023 columns->Current = 0;
8024 columns->LineMinY = columns->LineMaxY;
8025 }
8026 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8027 window->DC.CursorPos.y = columns->LineMinY;
8028 window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
8029 window->DC.CurrentLineTextBaseOffset = 0.0f;
8030
8031 PushColumnClipRect();
8032 PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
8033 }
8034
GetColumnIndex()8035 int ImGui::GetColumnIndex()
8036 {
8037 ImGuiWindow* window = GetCurrentWindowRead();
8038 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
8039 }
8040
GetColumnsCount()8041 int ImGui::GetColumnsCount()
8042 {
8043 ImGuiWindow* window = GetCurrentWindowRead();
8044 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
8045 }
8046
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)8047 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
8048 {
8049 return offset_norm * (columns->MaxX - columns->MinX);
8050 }
8051
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)8052 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
8053 {
8054 return offset / (columns->MaxX - columns->MinX);
8055 }
8056
GetColumnsRectHalfWidth()8057 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
8058
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)8059 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
8060 {
8061 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
8062 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
8063 ImGuiContext& g = *GImGui;
8064 ImGuiWindow* window = g.CurrentWindow;
8065 IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
8066 IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
8067
8068 float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
8069 x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
8070 if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
8071 x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
8072
8073 return x;
8074 }
8075
GetColumnOffset(int column_index)8076 float ImGui::GetColumnOffset(int column_index)
8077 {
8078 ImGuiWindow* window = GetCurrentWindowRead();
8079 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8080 IM_ASSERT(columns != NULL);
8081
8082 if (column_index < 0)
8083 column_index = columns->Current;
8084 IM_ASSERT(column_index < columns->Columns.Size);
8085
8086 const float t = columns->Columns[column_index].OffsetNorm;
8087 const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
8088 return x_offset;
8089 }
8090
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)8091 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
8092 {
8093 if (column_index < 0)
8094 column_index = columns->Current;
8095
8096 float offset_norm;
8097 if (before_resize)
8098 offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
8099 else
8100 offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
8101 return OffsetNormToPixels(columns, offset_norm);
8102 }
8103
GetColumnWidth(int column_index)8104 float ImGui::GetColumnWidth(int column_index)
8105 {
8106 ImGuiWindow* window = GetCurrentWindowRead();
8107 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8108 IM_ASSERT(columns != NULL);
8109
8110 if (column_index < 0)
8111 column_index = columns->Current;
8112 return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
8113 }
8114
SetColumnOffset(int column_index,float offset)8115 void ImGui::SetColumnOffset(int column_index, float offset)
8116 {
8117 ImGuiContext& g = *GImGui;
8118 ImGuiWindow* window = g.CurrentWindow;
8119 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8120 IM_ASSERT(columns != NULL);
8121
8122 if (column_index < 0)
8123 column_index = columns->Current;
8124 IM_ASSERT(column_index < columns->Columns.Size);
8125
8126 const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
8127 const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
8128
8129 if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
8130 offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
8131 columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
8132
8133 if (preserve_width)
8134 SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
8135 }
8136
SetColumnWidth(int column_index,float width)8137 void ImGui::SetColumnWidth(int column_index, float width)
8138 {
8139 ImGuiWindow* window = GetCurrentWindowRead();
8140 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8141 IM_ASSERT(columns != NULL);
8142
8143 if (column_index < 0)
8144 column_index = columns->Current;
8145 SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
8146 }
8147
PushColumnClipRect(int column_index)8148 void ImGui::PushColumnClipRect(int column_index)
8149 {
8150 ImGuiWindow* window = GetCurrentWindowRead();
8151 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8152 if (column_index < 0)
8153 column_index = columns->Current;
8154
8155 PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
8156 }
8157
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)8158 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
8159 {
8160 for (int n = 0; n < window->ColumnsStorage.Size; n++)
8161 if (window->ColumnsStorage[n].ID == id)
8162 return &window->ColumnsStorage[n];
8163
8164 window->ColumnsStorage.push_back(ImGuiColumnsSet());
8165 ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
8166 columns->ID = id;
8167 return columns;
8168 }
8169
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)8170 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
8171 {
8172 ImGuiContext& g = *GImGui;
8173 ImGuiWindow* window = GetCurrentWindow();
8174
8175 IM_ASSERT(columns_count > 1);
8176 IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
8177
8178 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
8179 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
8180 PushID(0x11223347 + (str_id ? 0 : columns_count));
8181 ImGuiID id = window->GetID(str_id ? str_id : "columns");
8182 PopID();
8183
8184 // Acquire storage for the columns set
8185 ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
8186 IM_ASSERT(columns->ID == id);
8187 columns->Current = 0;
8188 columns->Count = columns_count;
8189 columns->Flags = flags;
8190 window->DC.ColumnsSet = columns;
8191
8192 // Set state for first column
8193 const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
8194 columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
8195 columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
8196 columns->StartPosY = window->DC.CursorPos.y;
8197 columns->StartMaxPosX = window->DC.CursorMaxPos.x;
8198 columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
8199 window->DC.ColumnsOffset.x = 0.0f;
8200 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8201
8202 // Clear data if columns count changed
8203 if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
8204 columns->Columns.resize(0);
8205
8206 // Initialize defaults
8207 columns->IsFirstFrame = (columns->Columns.Size == 0);
8208 if (columns->Columns.Size == 0)
8209 {
8210 columns->Columns.reserve(columns_count + 1);
8211 for (int n = 0; n < columns_count + 1; n++)
8212 {
8213 ImGuiColumnData column;
8214 column.OffsetNorm = n / (float)columns_count;
8215 columns->Columns.push_back(column);
8216 }
8217 }
8218
8219 for (int n = 0; n < columns_count; n++)
8220 {
8221 // Compute clipping rectangle
8222 ImGuiColumnData* column = &columns->Columns[n];
8223 float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
8224 float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
8225 column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
8226 column->ClipRect.ClipWith(window->ClipRect);
8227 }
8228
8229 window->DrawList->ChannelsSplit(columns->Count);
8230 PushColumnClipRect();
8231 PushItemWidth(GetColumnWidth() * 0.65f);
8232 }
8233
EndColumns()8234 void ImGui::EndColumns()
8235 {
8236 ImGuiContext& g = *GImGui;
8237 ImGuiWindow* window = GetCurrentWindow();
8238 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
8239 IM_ASSERT(columns != NULL);
8240
8241 PopItemWidth();
8242 PopClipRect();
8243 window->DrawList->ChannelsMerge();
8244
8245 columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
8246 window->DC.CursorPos.y = columns->LineMaxY;
8247 if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
8248 window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent
8249
8250 // Draw columns borders and handle resize
8251 bool is_being_resized = false;
8252 if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
8253 {
8254 const float y1 = columns->StartPosY;
8255 const float y2 = window->DC.CursorPos.y;
8256 int dragging_column = -1;
8257 for (int n = 1; n < columns->Count; n++)
8258 {
8259 float x = window->Pos.x + GetColumnOffset(n);
8260 const ImGuiID column_id = columns->ID + ImGuiID(n);
8261 const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
8262 const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
8263 KeepAliveID(column_id);
8264 if (IsClippedEx(column_rect, column_id, false))
8265 continue;
8266
8267 bool hovered = false, held = false;
8268 if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
8269 {
8270 ButtonBehavior(column_rect, column_id, &hovered, &held);
8271 if (hovered || held)
8272 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
8273 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
8274 dragging_column = n;
8275 }
8276
8277 // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
8278 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
8279 const float xi = (float)(int)x;
8280 window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
8281 }
8282
8283 // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
8284 if (dragging_column != -1)
8285 {
8286 if (!columns->IsBeingResized)
8287 for (int n = 0; n < columns->Count + 1; n++)
8288 columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
8289 columns->IsBeingResized = is_being_resized = true;
8290 float x = GetDraggedColumnOffset(columns, dragging_column);
8291 SetColumnOffset(dragging_column, x);
8292 }
8293 }
8294 columns->IsBeingResized = is_being_resized;
8295
8296 window->DC.ColumnsSet = NULL;
8297 window->DC.ColumnsOffset.x = 0.0f;
8298 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
8299 }
8300
8301 // [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing]
Columns(int columns_count,const char * id,bool border)8302 void ImGui::Columns(int columns_count, const char* id, bool border)
8303 {
8304 ImGuiWindow* window = GetCurrentWindow();
8305 IM_ASSERT(columns_count >= 1);
8306
8307 ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
8308 //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
8309 if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
8310 return;
8311
8312 if (window->DC.ColumnsSet != NULL)
8313 EndColumns();
8314
8315 if (columns_count != 1)
8316 BeginColumns(id, columns_count, flags);
8317 }
8318
8319 //-----------------------------------------------------------------------------
8320 // [SECTION] DRAG AND DROP
8321 //-----------------------------------------------------------------------------
8322
ClearDragDrop()8323 void ImGui::ClearDragDrop()
8324 {
8325 ImGuiContext& g = *GImGui;
8326 g.DragDropActive = false;
8327 g.DragDropPayload.Clear();
8328 g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
8329 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
8330 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
8331 g.DragDropAcceptFrameCount = -1;
8332
8333 g.DragDropPayloadBufHeap.clear();
8334 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8335 }
8336
8337 // Call when current ID is active.
8338 // 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)8339 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
8340 {
8341 ImGuiContext& g = *GImGui;
8342 ImGuiWindow* window = g.CurrentWindow;
8343
8344 bool source_drag_active = false;
8345 ImGuiID source_id = 0;
8346 ImGuiID source_parent_id = 0;
8347 int mouse_button = 0;
8348 if (!(flags & ImGuiDragDropFlags_SourceExtern))
8349 {
8350 source_id = window->DC.LastItemId;
8351 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
8352 return false;
8353 if (g.IO.MouseDown[mouse_button] == false)
8354 return false;
8355
8356 if (source_id == 0)
8357 {
8358 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8359 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8360 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8361 {
8362 IM_ASSERT(0);
8363 return false;
8364 }
8365
8366 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8367 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8368 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8369 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8370 bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8371 if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8372 return false;
8373 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8374 if (is_hovered)
8375 SetHoveredID(source_id);
8376 if (is_hovered && g.IO.MouseClicked[mouse_button])
8377 {
8378 SetActiveID(source_id, window);
8379 FocusWindow(window);
8380 }
8381 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8382 g.ActiveIdAllowOverlap = is_hovered;
8383 }
8384 else
8385 {
8386 g.ActiveIdAllowOverlap = false;
8387 }
8388 if (g.ActiveId != source_id)
8389 return false;
8390 source_parent_id = window->IDStack.back();
8391 source_drag_active = IsMouseDragging(mouse_button);
8392 }
8393 else
8394 {
8395 window = NULL;
8396 source_id = ImHash("#SourceExtern", 0);
8397 source_drag_active = true;
8398 }
8399
8400 if (source_drag_active)
8401 {
8402 if (!g.DragDropActive)
8403 {
8404 IM_ASSERT(source_id != 0);
8405 ClearDragDrop();
8406 ImGuiPayload& payload = g.DragDropPayload;
8407 payload.SourceId = source_id;
8408 payload.SourceParentId = source_parent_id;
8409 g.DragDropActive = true;
8410 g.DragDropSourceFlags = flags;
8411 g.DragDropMouseButton = mouse_button;
8412 }
8413 g.DragDropSourceFrameCount = g.FrameCount;
8414 g.DragDropWithinSourceOrTarget = true;
8415
8416 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8417 {
8418 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8419 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8420 BeginTooltip();
8421 if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8422 {
8423 ImGuiWindow* tooltip_window = g.CurrentWindow;
8424 tooltip_window->SkipItems = true;
8425 tooltip_window->HiddenFramesRegular = 1;
8426 }
8427 }
8428
8429 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8430 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8431
8432 return true;
8433 }
8434 return false;
8435 }
8436
EndDragDropSource()8437 void ImGui::EndDragDropSource()
8438 {
8439 ImGuiContext& g = *GImGui;
8440 IM_ASSERT(g.DragDropActive);
8441 IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8442
8443 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8444 EndTooltip();
8445
8446 // Discard the drag if have not called SetDragDropPayload()
8447 if (g.DragDropPayload.DataFrameCount == -1)
8448 ClearDragDrop();
8449 g.DragDropWithinSourceOrTarget = false;
8450 }
8451
8452 // 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)8453 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8454 {
8455 ImGuiContext& g = *GImGui;
8456 ImGuiPayload& payload = g.DragDropPayload;
8457 if (cond == 0)
8458 cond = ImGuiCond_Always;
8459
8460 IM_ASSERT(type != NULL);
8461 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8462 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8463 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8464 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
8465
8466 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8467 {
8468 // Copy payload
8469 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8470 g.DragDropPayloadBufHeap.resize(0);
8471 if (data_size > sizeof(g.DragDropPayloadBufLocal))
8472 {
8473 // Store in heap
8474 g.DragDropPayloadBufHeap.resize((int)data_size);
8475 payload.Data = g.DragDropPayloadBufHeap.Data;
8476 memcpy(payload.Data, data, data_size);
8477 }
8478 else if (data_size > 0)
8479 {
8480 // Store locally
8481 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8482 payload.Data = g.DragDropPayloadBufLocal;
8483 memcpy(payload.Data, data, data_size);
8484 }
8485 else
8486 {
8487 payload.Data = NULL;
8488 }
8489 payload.DataSize = (int)data_size;
8490 }
8491 payload.DataFrameCount = g.FrameCount;
8492
8493 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8494 }
8495
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8496 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8497 {
8498 ImGuiContext& g = *GImGui;
8499 if (!g.DragDropActive)
8500 return false;
8501
8502 ImGuiWindow* window = g.CurrentWindow;
8503 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8504 return false;
8505 IM_ASSERT(id != 0);
8506 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8507 return false;
8508 if (window->SkipItems)
8509 return false;
8510
8511 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8512 g.DragDropTargetRect = bb;
8513 g.DragDropTargetId = id;
8514 g.DragDropWithinSourceOrTarget = true;
8515 return true;
8516 }
8517
8518 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8519 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8520 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8521 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8522 bool ImGui::BeginDragDropTarget()
8523 {
8524 ImGuiContext& g = *GImGui;
8525 if (!g.DragDropActive)
8526 return false;
8527
8528 ImGuiWindow* window = g.CurrentWindow;
8529 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8530 return false;
8531 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8532 return false;
8533
8534 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8535 ImGuiID id = window->DC.LastItemId;
8536 if (id == 0)
8537 id = window->GetIDFromRectangle(display_rect);
8538 if (g.DragDropPayload.SourceId == id)
8539 return false;
8540
8541 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8542 g.DragDropTargetRect = display_rect;
8543 g.DragDropTargetId = id;
8544 g.DragDropWithinSourceOrTarget = true;
8545 return true;
8546 }
8547
IsDragDropPayloadBeingAccepted()8548 bool ImGui::IsDragDropPayloadBeingAccepted()
8549 {
8550 ImGuiContext& g = *GImGui;
8551 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8552 }
8553
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8554 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8555 {
8556 ImGuiContext& g = *GImGui;
8557 ImGuiWindow* window = g.CurrentWindow;
8558 ImGuiPayload& payload = g.DragDropPayload;
8559 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8560 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
8561 if (type != NULL && !payload.IsDataType(type))
8562 return NULL;
8563
8564 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8565 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8566 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8567 ImRect r = g.DragDropTargetRect;
8568 float r_surface = r.GetWidth() * r.GetHeight();
8569 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8570 {
8571 g.DragDropAcceptFlags = flags;
8572 g.DragDropAcceptIdCurr = g.DragDropTargetId;
8573 g.DragDropAcceptIdCurrRectSurface = r_surface;
8574 }
8575
8576 // Render default drop visuals
8577 payload.Preview = was_accepted_previously;
8578 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8579 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8580 {
8581 // FIXME-DRAG: Settle on a proper default visuals for drop target.
8582 r.Expand(3.5f);
8583 bool push_clip_rect = !window->ClipRect.Contains(r);
8584 if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8585 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8586 if (push_clip_rect) window->DrawList->PopClipRect();
8587 }
8588
8589 g.DragDropAcceptFrameCount = g.FrameCount;
8590 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()
8591 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8592 return NULL;
8593
8594 return &payload;
8595 }
8596
GetDragDropPayload()8597 const ImGuiPayload* ImGui::GetDragDropPayload()
8598 {
8599 ImGuiContext& g = *GImGui;
8600 return g.DragDropActive ? &g.DragDropPayload : NULL;
8601 }
8602
8603 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8604 void ImGui::EndDragDropTarget()
8605 {
8606 ImGuiContext& g = *GImGui;
8607 IM_ASSERT(g.DragDropActive);
8608 IM_ASSERT(g.DragDropWithinSourceOrTarget);
8609 g.DragDropWithinSourceOrTarget = false;
8610 }
8611
8612 //-----------------------------------------------------------------------------
8613 // [SECTION] DOCKING
8614 //-----------------------------------------------------------------------------
8615
8616 // (this section is filled in the 'docking' branch)
8617
8618 //-----------------------------------------------------------------------------
8619 // [SECTION] LOGGING/CAPTURING
8620 //-----------------------------------------------------------------------------
8621
8622 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8623 void ImGui::LogText(const char* fmt, ...)
8624 {
8625 ImGuiContext& g = *GImGui;
8626 if (!g.LogEnabled)
8627 return;
8628
8629 va_list args;
8630 va_start(args, fmt);
8631 if (g.LogFile)
8632 vfprintf(g.LogFile, fmt, args);
8633 else
8634 g.LogClipboard.appendfv(fmt, args);
8635 va_end(args);
8636 }
8637
8638 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
8639 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)8640 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8641 {
8642 ImGuiContext& g = *GImGui;
8643 ImGuiWindow* window = g.CurrentWindow;
8644
8645 if (!text_end)
8646 text_end = FindRenderedTextEnd(text, text_end);
8647
8648 const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8649 if (ref_pos)
8650 window->DC.LogLinePosY = ref_pos->y;
8651
8652 const char* text_remaining = text;
8653 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
8654 g.LogStartDepth = window->DC.TreeDepth;
8655 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8656 for (;;)
8657 {
8658 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8659 const char* line_start = text_remaining;
8660 const char* line_end = ImStreolRange(line_start, text_end);
8661 const bool is_first_line = (line_start == text);
8662 const bool is_last_line = (line_end == text_end);
8663 if (!is_last_line || (line_start != line_end))
8664 {
8665 const int char_count = (int)(line_end - line_start);
8666 if (log_new_line || !is_first_line)
8667 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start);
8668 else
8669 LogText(" %.*s", char_count, line_start);
8670 }
8671
8672 if (is_last_line)
8673 break;
8674 text_remaining = line_end + 1;
8675 }
8676 }
8677
8678 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8679 void ImGui::LogToTTY(int max_depth)
8680 {
8681 ImGuiContext& g = *GImGui;
8682 if (g.LogEnabled)
8683 return;
8684 ImGuiWindow* window = g.CurrentWindow;
8685
8686 IM_ASSERT(g.LogFile == NULL);
8687 g.LogFile = stdout;
8688 g.LogEnabled = true;
8689 g.LogStartDepth = window->DC.TreeDepth;
8690 if (max_depth >= 0)
8691 g.LogAutoExpandMaxDepth = max_depth;
8692 }
8693
8694 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8695 void ImGui::LogToFile(int max_depth, const char* filename)
8696 {
8697 ImGuiContext& g = *GImGui;
8698 if (g.LogEnabled)
8699 return;
8700 ImGuiWindow* window = g.CurrentWindow;
8701
8702 if (!filename)
8703 {
8704 filename = g.IO.LogFilename;
8705 if (!filename)
8706 return;
8707 }
8708
8709 IM_ASSERT(g.LogFile == NULL);
8710 g.LogFile = ImFileOpen(filename, "ab");
8711 if (!g.LogFile)
8712 {
8713 IM_ASSERT(g.LogFile != NULL); // Consider this an error
8714 return;
8715 }
8716 g.LogEnabled = true;
8717 g.LogStartDepth = window->DC.TreeDepth;
8718 if (max_depth >= 0)
8719 g.LogAutoExpandMaxDepth = max_depth;
8720 }
8721
8722 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8723 void ImGui::LogToClipboard(int max_depth)
8724 {
8725 ImGuiContext& g = *GImGui;
8726 if (g.LogEnabled)
8727 return;
8728 ImGuiWindow* window = g.CurrentWindow;
8729
8730 IM_ASSERT(g.LogFile == NULL);
8731 g.LogFile = NULL;
8732 g.LogEnabled = true;
8733 g.LogStartDepth = window->DC.TreeDepth;
8734 if (max_depth >= 0)
8735 g.LogAutoExpandMaxDepth = max_depth;
8736 }
8737
LogFinish()8738 void ImGui::LogFinish()
8739 {
8740 ImGuiContext& g = *GImGui;
8741 if (!g.LogEnabled)
8742 return;
8743
8744 LogText(IM_NEWLINE);
8745 if (g.LogFile != NULL)
8746 {
8747 if (g.LogFile == stdout)
8748 fflush(g.LogFile);
8749 else
8750 fclose(g.LogFile);
8751 g.LogFile = NULL;
8752 }
8753 if (g.LogClipboard.size() > 1)
8754 {
8755 SetClipboardText(g.LogClipboard.begin());
8756 g.LogClipboard.clear();
8757 }
8758 g.LogEnabled = false;
8759 }
8760
8761 // Helper to display logging buttons
LogButtons()8762 void ImGui::LogButtons()
8763 {
8764 ImGuiContext& g = *GImGui;
8765
8766 PushID("LogButtons");
8767 const bool log_to_tty = Button("Log To TTY"); SameLine();
8768 const bool log_to_file = Button("Log To File"); SameLine();
8769 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8770 PushItemWidth(80.0f);
8771 PushAllowKeyboardFocus(false);
8772 SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8773 PopAllowKeyboardFocus();
8774 PopItemWidth();
8775 PopID();
8776
8777 // Start logging at the end of the function so that the buttons don't appear in the log
8778 if (log_to_tty)
8779 LogToTTY(g.LogAutoExpandMaxDepth);
8780 if (log_to_file)
8781 LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8782 if (log_to_clipboard)
8783 LogToClipboard(g.LogAutoExpandMaxDepth);
8784 }
8785
8786 //-----------------------------------------------------------------------------
8787 // [SECTION] SETTINGS
8788 //-----------------------------------------------------------------------------
8789
MarkIniSettingsDirty()8790 void ImGui::MarkIniSettingsDirty()
8791 {
8792 ImGuiContext& g = *GImGui;
8793 if (g.SettingsDirtyTimer <= 0.0f)
8794 g.SettingsDirtyTimer = g.IO.IniSavingRate;
8795 }
8796
MarkIniSettingsDirty(ImGuiWindow * window)8797 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8798 {
8799 ImGuiContext& g = *GImGui;
8800 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8801 if (g.SettingsDirtyTimer <= 0.0f)
8802 g.SettingsDirtyTimer = g.IO.IniSavingRate;
8803 }
8804
CreateNewWindowSettings(const char * name)8805 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
8806 {
8807 ImGuiContext& g = *GImGui;
8808 g.SettingsWindows.push_back(ImGuiWindowSettings());
8809 ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8810 settings->Name = ImStrdup(name);
8811 settings->ID = ImHash(name, 0);
8812 return settings;
8813 }
8814
FindWindowSettings(ImGuiID id)8815 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8816 {
8817 ImGuiContext& g = *GImGui;
8818 for (int i = 0; i != g.SettingsWindows.Size; i++)
8819 if (g.SettingsWindows[i].ID == id)
8820 return &g.SettingsWindows[i];
8821 return NULL;
8822 }
8823
LoadIniSettingsFromDisk(const char * ini_filename)8824 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8825 {
8826 size_t file_data_size = 0;
8827 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8828 if (!file_data)
8829 return;
8830 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8831 ImGui::MemFree(file_data);
8832 }
8833
FindSettingsHandler(const char * type_name)8834 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8835 {
8836 ImGuiContext& g = *GImGui;
8837 const ImGuiID type_hash = ImHash(type_name, 0, 0);
8838 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8839 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
8840 return &g.SettingsHandlers[handler_n];
8841 return NULL;
8842 }
8843
8844 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)8845 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
8846 {
8847 ImGuiContext& g = *GImGui;
8848 IM_ASSERT(g.Initialized);
8849 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
8850
8851 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
8852 // 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..
8853 if (ini_size == 0)
8854 ini_size = strlen(ini_data);
8855 char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
8856 char* buf_end = buf + ini_size;
8857 memcpy(buf, ini_data, ini_size);
8858 buf[ini_size] = 0;
8859
8860 void* entry_data = NULL;
8861 ImGuiSettingsHandler* entry_handler = NULL;
8862
8863 char* line_end = NULL;
8864 for (char* line = buf; line < buf_end; line = line_end + 1)
8865 {
8866 // Skip new lines markers, then find end of the line
8867 while (*line == '\n' || *line == '\r')
8868 line++;
8869 line_end = line;
8870 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
8871 line_end++;
8872 line_end[0] = 0;
8873 if (line[0] == ';')
8874 continue;
8875 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
8876 {
8877 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
8878 line_end[-1] = 0;
8879 const char* name_end = line_end - 1;
8880 const char* type_start = line + 1;
8881 char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
8882 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
8883 if (!type_end || !name_start)
8884 {
8885 name_start = type_start; // Import legacy entries that have no type
8886 type_start = "Window";
8887 }
8888 else
8889 {
8890 *type_end = 0; // Overwrite first ']'
8891 name_start++; // Skip second '['
8892 }
8893 entry_handler = FindSettingsHandler(type_start);
8894 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
8895 }
8896 else if (entry_handler != NULL && entry_data != NULL)
8897 {
8898 // Let type handler parse the line
8899 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
8900 }
8901 }
8902 ImGui::MemFree(buf);
8903 g.SettingsLoaded = true;
8904 }
8905
SaveIniSettingsToDisk(const char * ini_filename)8906 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
8907 {
8908 ImGuiContext& g = *GImGui;
8909 g.SettingsDirtyTimer = 0.0f;
8910 if (!ini_filename)
8911 return;
8912
8913 size_t ini_data_size = 0;
8914 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
8915 FILE* f = ImFileOpen(ini_filename, "wt");
8916 if (!f)
8917 return;
8918 fwrite(ini_data, sizeof(char), ini_data_size, f);
8919 fclose(f);
8920 }
8921
8922 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)8923 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
8924 {
8925 ImGuiContext& g = *GImGui;
8926 g.SettingsDirtyTimer = 0.0f;
8927 g.SettingsIniData.Buf.resize(0);
8928 g.SettingsIniData.Buf.push_back(0);
8929 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8930 {
8931 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
8932 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
8933 }
8934 if (out_size)
8935 *out_size = (size_t)g.SettingsIniData.size();
8936 return g.SettingsIniData.c_str();
8937 }
8938
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)8939 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
8940 {
8941 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
8942 if (!settings)
8943 settings = ImGui::CreateNewWindowSettings(name);
8944 return (void*)settings;
8945 }
8946
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)8947 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
8948 {
8949 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
8950 float x, y;
8951 int i;
8952 if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
8953 else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
8954 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
8955 }
8956
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)8957 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
8958 {
8959 // Gather data from windows that were active during this session
8960 // (if a window wasn't opened in this session we preserve its settings)
8961 ImGuiContext& g = *imgui_ctx;
8962 for (int i = 0; i != g.Windows.Size; i++)
8963 {
8964 ImGuiWindow* window = g.Windows[i];
8965 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
8966 continue;
8967
8968 ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
8969 if (!settings)
8970 {
8971 settings = ImGui::CreateNewWindowSettings(window->Name);
8972 window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings);
8973 }
8974 IM_ASSERT(settings->ID == window->ID);
8975 settings->Pos = window->Pos;
8976 settings->Size = window->SizeFull;
8977 settings->Collapsed = window->Collapsed;
8978 }
8979
8980 // Write to text buffer
8981 buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
8982 for (int i = 0; i != g.SettingsWindows.Size; i++)
8983 {
8984 const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
8985 if (settings->Pos.x == FLT_MAX)
8986 continue;
8987 const char* name = settings->Name;
8988 if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
8989 name = p;
8990 buf->appendf("[%s][%s]\n", handler->TypeName, name);
8991 buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
8992 buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
8993 buf->appendf("Collapsed=%d\n", settings->Collapsed);
8994 buf->appendf("\n");
8995 }
8996 }
8997
8998 //-----------------------------------------------------------------------------
8999 // [SECTION] PLATFORM DEPENDENT HELPERS
9000 //-----------------------------------------------------------------------------
9001
9002 #if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
9003 #ifndef WIN32_LEAN_AND_MEAN
9004 #define WIN32_LEAN_AND_MEAN
9005 #endif
9006 #ifndef __MINGW32__
9007 #include <Windows.h>
9008 #else
9009 #include <windows.h>
9010 #endif
9011 #endif
9012
9013 // Win32 API clipboard implementation
9014 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
9015
9016 #ifdef _MSC_VER
9017 #pragma comment(lib, "user32")
9018 #endif
9019
GetClipboardTextFn_DefaultImpl(void *)9020 static const char* GetClipboardTextFn_DefaultImpl(void*)
9021 {
9022 static ImVector<char> buf_local;
9023 buf_local.clear();
9024 if (!::OpenClipboard(NULL))
9025 return NULL;
9026 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
9027 if (wbuf_handle == NULL)
9028 {
9029 ::CloseClipboard();
9030 return NULL;
9031 }
9032 if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
9033 {
9034 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
9035 buf_local.resize(buf_len);
9036 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
9037 }
9038 ::GlobalUnlock(wbuf_handle);
9039 ::CloseClipboard();
9040 return buf_local.Data;
9041 }
9042
SetClipboardTextFn_DefaultImpl(void *,const char * text)9043 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9044 {
9045 if (!::OpenClipboard(NULL))
9046 return;
9047 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
9048 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
9049 if (wbuf_handle == NULL)
9050 {
9051 ::CloseClipboard();
9052 return;
9053 }
9054 ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
9055 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
9056 ::GlobalUnlock(wbuf_handle);
9057 ::EmptyClipboard();
9058 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
9059 ::GlobalFree(wbuf_handle);
9060 ::CloseClipboard();
9061 }
9062
9063 #else
9064
9065 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)9066 static const char* GetClipboardTextFn_DefaultImpl(void*)
9067 {
9068 ImGuiContext& g = *GImGui;
9069 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
9070 }
9071
9072 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)9073 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
9074 {
9075 ImGuiContext& g = *GImGui;
9076 g.PrivateClipboard.clear();
9077 const char* text_end = text + strlen(text);
9078 g.PrivateClipboard.resize((int)(text_end - text) + 1);
9079 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
9080 g.PrivateClipboard[(int)(text_end - text)] = 0;
9081 }
9082
9083 #endif
9084
9085 // Win32 API IME support (for Asian languages, etc.)
9086 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
9087
9088 #include <imm.h>
9089 #ifdef _MSC_VER
9090 #pragma comment(lib, "imm32")
9091 #endif
9092
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)9093 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
9094 {
9095 // Notify OS Input Method Editor of text input position
9096 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
9097 if (HIMC himc = ::ImmGetContext(hwnd))
9098 {
9099 COMPOSITIONFORM cf;
9100 cf.ptCurrentPos.x = x;
9101 cf.ptCurrentPos.y = y;
9102 cf.dwStyle = CFS_FORCE_POSITION;
9103 ::ImmSetCompositionWindow(himc, &cf);
9104 ::ImmReleaseContext(hwnd, himc);
9105 }
9106 }
9107
9108 #else
9109
ImeSetInputScreenPosFn_DefaultImpl(int,int)9110 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
9111
9112 #endif
9113
9114 //-----------------------------------------------------------------------------
9115 // [SECTION] METRICS/DEBUG WINDOW
9116 //-----------------------------------------------------------------------------
9117
ShowMetricsWindow(bool * p_open)9118 void ImGui::ShowMetricsWindow(bool* p_open)
9119 {
9120 if (!ImGui::Begin("ImGui Metrics", p_open))
9121 {
9122 ImGui::End();
9123 return;
9124 }
9125
9126 static bool show_draw_cmd_clip_rects = true;
9127 static bool show_window_begin_order = false;
9128 ImGuiIO& io = ImGui::GetIO();
9129 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
9130 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
9131 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
9132 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
9133 ImGui::Text("%d allocations", io.MetricsActiveAllocations);
9134 ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
9135 ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
9136 ImGui::Separator();
9137
9138 struct Funcs
9139 {
9140 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
9141 {
9142 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);
9143 if (draw_list == ImGui::GetWindowDrawList())
9144 {
9145 ImGui::SameLine();
9146 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
9147 if (node_open) ImGui::TreePop();
9148 return;
9149 }
9150
9151 ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list
9152 if (window && IsItemHovered())
9153 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
9154 if (!node_open)
9155 return;
9156
9157 int elem_offset = 0;
9158 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
9159 {
9160 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
9161 continue;
9162 if (pcmd->UserCallback)
9163 {
9164 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
9165 continue;
9166 }
9167 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
9168 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
9169 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
9170 {
9171 ImRect clip_rect = pcmd->ClipRect;
9172 ImRect vtxs_rect;
9173 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
9174 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
9175 clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
9176 vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
9177 }
9178 if (!pcmd_node_open)
9179 continue;
9180
9181 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
9182 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
9183 while (clipper.Step())
9184 for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
9185 {
9186 char buf[300];
9187 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
9188 ImVec2 triangles_pos[3];
9189 for (int n = 0; n < 3; n++, vtx_i++)
9190 {
9191 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
9192 triangles_pos[n] = v.pos;
9193 buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
9194 }
9195 ImGui::Selectable(buf, false);
9196 if (ImGui::IsItemHovered())
9197 {
9198 ImDrawListFlags backup_flags = overlay_draw_list->Flags;
9199 overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
9200 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
9201 overlay_draw_list->Flags = backup_flags;
9202 }
9203 }
9204 ImGui::TreePop();
9205 }
9206 ImGui::TreePop();
9207 }
9208
9209 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
9210 {
9211 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
9212 return;
9213 for (int i = 0; i < windows.Size; i++)
9214 Funcs::NodeWindow(windows[i], "Window");
9215 ImGui::TreePop();
9216 }
9217
9218 static void NodeWindow(ImGuiWindow* window, const char* label)
9219 {
9220 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
9221 return;
9222 ImGuiWindowFlags flags = window->Flags;
9223 NodeDrawList(window, window->DrawList, "DrawList");
9224 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y);
9225 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
9226 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
9227 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
9228 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
9229 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window));
9230 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
9231 ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
9232 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
9233 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
9234 if (!window->NavRectRel[0].IsInverted())
9235 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);
9236 else
9237 ImGui::BulletText("NavRectRel[0]: <None>");
9238 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
9239 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
9240 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
9241 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
9242 {
9243 for (int n = 0; n < window->ColumnsStorage.Size; n++)
9244 {
9245 const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
9246 if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
9247 {
9248 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
9249 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
9250 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
9251 ImGui::TreePop();
9252 }
9253 }
9254 ImGui::TreePop();
9255 }
9256 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
9257 ImGui::TreePop();
9258 }
9259
9260 static void NodeTabBar(ImGuiTabBar* tab_bar)
9261 {
9262 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernable strings.
9263 char buf[256];
9264 char* p = buf;
9265 const char* buf_end = buf + IM_ARRAYSIZE(buf);
9266 p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s",
9267 tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : "");
9268 if (ImGui::TreeNode(tab_bar, "%s", buf))
9269 {
9270 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9271 {
9272 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9273 ImGui::PushID(tab);
9274 if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2);
9275 if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine();
9276 ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID);
9277 ImGui::PopID();
9278 }
9279 ImGui::TreePop();
9280 }
9281 }
9282 };
9283
9284 // Access private state, we are going to display the draw lists from last frame
9285 ImGuiContext& g = *GImGui;
9286 Funcs::NodeWindows(g.Windows, "Windows");
9287 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
9288 {
9289 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
9290 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
9291 ImGui::TreePop();
9292 }
9293 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
9294 {
9295 for (int i = 0; i < g.OpenPopupStack.Size; i++)
9296 {
9297 ImGuiWindow* window = g.OpenPopupStack[i].Window;
9298 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" : "");
9299 }
9300 ImGui::TreePop();
9301 }
9302 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
9303 {
9304 for (int n = 0; n < g.TabBars.Data.Size; n++)
9305 Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
9306 ImGui::TreePop();
9307 }
9308 if (ImGui::TreeNode("Internal state"))
9309 {
9310 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
9311 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
9312 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
9313 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
9314 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]);
9315 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
9316 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
9317 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
9318 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
9319 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
9320 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
9321 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
9322 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
9323 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
9324 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
9325 ImGui::TreePop();
9326 }
9327
9328
9329 if (g.IO.KeyCtrl && show_window_begin_order)
9330 {
9331 for (int n = 0; n < g.Windows.Size; n++)
9332 {
9333 ImGuiWindow* window = g.Windows[n];
9334 if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
9335 continue;
9336 char buf[32];
9337 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
9338 float font_size = ImGui::GetFontSize() * 2;
9339 ImDrawList* overlay_draw_list = GetOverlayDrawList(window);
9340 overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
9341 overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
9342 }
9343 }
9344 ImGui::End();
9345 }
9346
9347 //-----------------------------------------------------------------------------
9348
9349 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
9350 // 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.
9351 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
9352 #include "imgui_user.inl"
9353 #endif
9354
9355 //-----------------------------------------------------------------------------
9356