1 // dear imgui, v1.65
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 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
11 // This library is free but I need your support to sustain development and maintenance.
12 // If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui
13
14 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
15 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
16 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
17 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
18 // to a better solution or official support for them.
19
20 /*
21
22 Index of this file:
23
24 DOCUMENTATION
25
26 - MISSION STATEMENT
27 - END-USER GUIDE
28 - PROGRAMMER GUIDE (read me!)
29 - Read first
30 - How to update to a newer version of Dear ImGui
31 - Getting started with integrating Dear ImGui in your code/engine
32 - This is how a simple application may look like (2 variations)
33 - This is how a simple rendering function may look like
34 - Using gamepad/keyboard navigation controls
35 - API BREAKING CHANGES (read me when you update!)
36 - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
37 - How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
38 - How can I display an image? What is ImTextureID, how does it works?
39 - How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack.
40 - How can I use my own math types instead of ImVec2/ImVec4?
41 - How can I load a different font than the default?
42 - How can I easily use icons in my application?
43 - How can I load multiple fonts?
44 - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
45 - How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
46 - I integrated Dear ImGui in my engine and the text or lines are blurry..
47 - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
48 - How can I help?
49
50 CODE
51 (search for "[SECTION]" in the code to find them)
52
53 // [SECTION] FORWARD DECLARATIONS
54 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
55 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
56 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
57 // [SECTION] MISC HELPER/UTILITIES (ImText* functions)
58 // [SECTION] MISC HELPER/UTILITIES (Color functions)
59 // [SECTION] ImGuiStorage
60 // [SECTION] ImGuiTextFilter
61 // [SECTION] ImGuiTextBuffer
62 // [SECTION] ImGuiListClipper
63 // [SECTION] RENDER HELPERS
64 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
65 // [SECTION] TOOLTIPS
66 // [SECTION] POPUPS
67 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
68 // [SECTION] COLUMNS
69 // [SECTION] DRAG AND DROP
70 // [SECTION] LOGGING/CAPTURING
71 // [SECTION] SETTINGS
72 // [SECTION] PLATFORM DEPENDENT HELPERS
73 // [SECTION] METRICS/DEBUG WINDOW
74
75 */
76
77 //-----------------------------------------------------------------------------
78 // DOCUMENTATION
79 //-----------------------------------------------------------------------------
80
81 /*
82
83 MISSION STATEMENT
84 =================
85
86 - Easy to use to create code-driven and data-driven tools
87 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools
88 - Easy to hack and improve
89 - Minimize screen real-estate usage
90 - Minimize setup and maintenance
91 - Minimize state storage on user side
92 - Portable, minimize dependencies, run on target (consoles, phones, etc.)
93 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,
94 opening a tree node for the first time, etc. but a typical frame should not allocate anything)
95
96 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
97 - Doesn't look fancy, doesn't animate
98 - Limited layout features, intricate layouts are typically crafted in code
99
100
101 END-USER GUIDE
102 ==============
103
104 - Double-click on title bar to collapse window.
105 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin().
106 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents).
107 - Click and drag on any empty space to move window.
108 - TAB/SHIFT+TAB to cycle through keyboard editable fields.
109 - CTRL+Click on a slider or drag box to input value as text.
110 - Use mouse wheel to scroll.
111 - Text editor:
112 - Hold SHIFT or use mouse to select text.
113 - CTRL+Left/Right to word jump.
114 - CTRL+Shift+Left/Right to select words.
115 - CTRL+A our Double-Click to select all.
116 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/
117 - CTRL+Z,CTRL+Y to undo/redo.
118 - ESCAPE to revert text to its original value.
119 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!)
120 - Controls are automatically adjusted for OSX to match standard OSX text editing operations.
121 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard.
122 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW
123
124
125 PROGRAMMER GUIDE
126 ================
127
128 READ FIRST
129
130 - Read the FAQ below this section!
131 - 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
132 or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs.
133 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
134 - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861
135
136 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
137
138 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h)
139 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
140 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
141 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
142 likely be a comment about it. Please report any issue to the GitHub page!
143 - Try to keep your copy of dear imgui reasonably up to date.
144
145 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
146
147 - Run and study the examples and demo to get acquainted with the library.
148 - Add the Dear ImGui source files to your projects or using your preferred build system.
149 It is recommended you build the .cpp files as part of your project and not as a library.
150 - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types.
151 - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder.
152 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
153 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
154 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
155 phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render().
156 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code.
157
158 THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
159 EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder)
160
161 // Application init: create a dear imgui context, setup some options, load fonts
162 ImGui::CreateContext();
163 ImGuiIO& io = ImGui::GetIO();
164 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls
165 // TODO: Fill optional fields of the io structure later.
166 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
167
168 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11)
169 ImGui_ImplWin32_Init(hwnd);
170 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
171
172 // Application main loop
173 while (true)
174 {
175 // Feed inputs to dear imgui, start new frame
176 ImGui_ImplDX11_NewFrame();
177 ImGui_ImplWin32_NewFrame();
178 ImGui::NewFrame();
179
180 // Any application code here
181 ImGui::Text("Hello, world!");
182
183 // Render dear imgui into screen
184 ImGui::Render();
185 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
186 g_pSwapChain->Present(1, 0);
187 }
188
189 // Shutdown
190 ImGui_ImplDX11_Shutdown();
191 ImGui_ImplWin32_Shutdown();
192 ImGui::DestroyContext();
193
194 THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE
195 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE
196
197 // Application init: create a dear imgui context, setup some options, load fonts
198 ImGui::CreateContext();
199 ImGuiIO& io = ImGui::GetIO();
200 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls
201 // TODO: Fill optional fields of the io structure later.
202 // TODO: Load TTF/OTF fonts if you don't want to use the default font.
203
204 // Build and load the texture atlas into a texture
205 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)
206 int width, height;
207 unsigned char* pixels = NULL;
208 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
209
210 // At this point you've got the texture data and you need to upload that your your graphic system:
211 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.
212 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ below for details about ImTextureID.
213 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)
214 io.Fonts->TexID = (void*)texture;
215
216 // Application main loop
217 while (true)
218 {
219 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
220 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings)
221 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds)
222 io.DisplaySize.x = 1920.0f; // set the current display width
223 io.DisplaySize.y = 1280.0f; // set the current display height here
224 io.MousePos = my_mouse_pos; // set the mouse position
225 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states
226 io.MouseDown[1] = my_mouse_buttons[1];
227
228 // Call NewFrame(), after this point you can use ImGui::* functions anytime
229 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere)
230 ImGui::NewFrame();
231
232 // Most of your application code here
233 ImGui::Text("Hello, world!");
234 MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
235 MyGameRender(); // may use any ImGui functions as well!
236
237 // Render imgui, swap buffers
238 // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code)
239 ImGui::EndFrame();
240 ImGui::Render();
241 ImDrawData* draw_data = ImGui::GetDrawData();
242 MyImGuiRenderFunction(draw_data);
243 SwapBuffers();
244 }
245
246 // Shutdown
247 ImGui::DestroyContext();
248
249 THIS HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE
250
251 void void MyImGuiRenderFunction(ImDrawData* draw_data)
252 {
253 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
254 // TODO: Setup viewport using draw_data->DisplaySize
255 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
256 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.
257 for (int n = 0; n < draw_data->CmdListsCount; n++)
258 {
259 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui
260 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui
261 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
262 {
263 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
264 if (pcmd->UserCallback)
265 {
266 pcmd->UserCallback(cmd_list, pcmd);
267 }
268 else
269 {
270 // The texture for the draw call is specified by pcmd->TextureId.
271 // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization.
272 MyEngineBindTexture(pcmd->TextureId);
273
274 // We are using scissoring to clip some objects. All low-level graphics API should supports it.
275 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches
276 // (some elements visible outside their bounds) but you can fix that once everywhere else works!
277 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize)
278 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize.
279 // However, in the interest of supporting multi-viewport applications in the future, always subtract draw_data->DisplayPos from
280 // clipping bounds to convert them to your viewport space.
281 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)
282 ImVec2 pos = draw_data->DisplayPos;
283 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));
284
285 // Render 'pcmd->ElemCount/3' indexed triangles.
286 // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices.
287 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer);
288 }
289 idx_buffer += pcmd->ElemCount;
290 }
291 }
292 }
293
294 - The examples/ folders contains many functional implementation of the pseudo-code above.
295 - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated.
296 They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application.
297 In both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags.
298 - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues!
299
300 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS
301
302 - The gamepad/keyboard navigation is fairly functional and keeps being improved.
303 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787
304 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
305 - Gamepad:
306 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable.
307 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame().
308 Note that io.NavInputs[] is cleared by EndFrame().
309 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values:
310 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks.
311 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone.
312 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.).
313 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW.
314 - 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
315 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
316 - Keyboard:
317 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
318 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
319 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
320 will be set. For more advanced uses, you may want to read from:
321 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
322 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used).
323 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions.
324 Please reach out if you think the game vs navigation input sharing could be improved.
325 - Mouse:
326 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback.
327 - 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.
328 - 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.
329 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements.
330 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.
331 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.
332 (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!)
333 (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
334 to set a boolean to ignore your other external mouse positions until the external source is moved again.)
335
336
337 API BREAKING CHANGES
338 ====================
339
340 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
341 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.
342 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.
343 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
344
345 - 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.
346 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
347 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
348 - 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.
349 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
350 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
351 - 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).
352 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
353 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
354 - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature.
355 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
356 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
357 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
358 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
359 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
360 - 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.
361 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
362 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.
363 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.
364 - 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",
365 consistent with other functions. Kept redirection functions (will obsolete).
366 - 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.
367 - 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).
368 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
369 - 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.
370 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
371 - 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.
372 - 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.
373 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
374 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
375 - removed Shutdown() function, as DestroyContext() serve this purpose.
376 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
377 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
378 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
379 - 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.
380 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
381 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
382 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
383 - 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.
384 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
385 - 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
386 - 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.
387 - 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.
388 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
389 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
390 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
391 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
392 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
393 - 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.
394 - 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.
395 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.
396 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
397 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
398 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
399 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
400 - 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.
401 - 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.
402 - 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.
403 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
404 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
405 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
406 - 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).
407 - 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)".
408 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
409 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
410 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
411 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
412 - 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.
413 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
414 - 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.
415 - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete).
416 - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
417 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
418 - 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.
419 - 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.
420 - 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))'
421 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
422 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
423 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
424 - 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().
425 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
426 - 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.
427 - 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.
428 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
429 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you.
430 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.
431 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.
432 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col)
433 {
434 float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a;
435 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);
436 }
437 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.
438 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
439 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
440 - 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).
441 - 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.
442 - 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).
443 - 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)
444 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
445 - 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.
446 - 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.
447 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
448 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
449 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
450 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.
451 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!
452 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
453 - 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.
454 - 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
455 - 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.
456 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
457 - 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.
458 this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
459 - 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.
460 - the signature of the io.RenderDrawListsFn handler has changed!
461 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
462 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
463 argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
464 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
465 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
466 - 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.
467 - 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!
468 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
469 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
470 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
471 - 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.
472 - 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
473 - 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!
474 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
475 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
476 - 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.
477 - 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.
478 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
479 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
480 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
481 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
482 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
483 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
484 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
485 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
486 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
487 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
488 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
489 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
490 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
491 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
492 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
493 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
494 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
495 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
496 font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>
497 became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier;
498 you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs.
499 it is now recommended that you sample the font texture with bilinear interpolation.
500 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID.
501 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
502 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
503 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
504 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
505 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
506 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
507 - 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)
508 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
509 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
510 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
511 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
512 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
513 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
514
515
516 FREQUENTLY ASKED QUESTIONS (FAQ), TIPS
517 ======================================
518
519 Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application?
520 A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } )
521 - 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.
522 - 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.
523 - 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).
524 Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false.
525 This is because imgui needs to detect that you clicked in the void to unfocus its windows.
526 Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!).
527 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.
528 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
529 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().
530 Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically
531 have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs
532 were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.)
533
534 Q: How can I display an image? What is ImTextureID, how does it works?
535 A: Short explanation:
536 - 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.
537 - Actual textures are identified in a way that is up to the user/engine.
538 - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason).
539 Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.
540
541 Long explanation:
542 - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices.
543 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
544 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.).
545 - 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.
546 We carry the information to identify a "texture" in the ImTextureID type.
547 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.
548 Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
549 - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying
550 an image from the end-user perspective. This is what the _examples_ rendering functions are using:
551
552 OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp)
553 DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp)
554 DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp)
555 DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp)
556
557 For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID.
558 Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure
559 tying together both the texture and information about its format and how to read it.
560 - 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
561 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
562 is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them.
563 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
564 representation suggested by the example bindings is probably the best choice.
565 (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
566
567 User code may do:
568
569 // Cast our texture type to ImTextureID / void*
570 MyTexture* texture = g_CoffeeTableTexture;
571 ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height));
572
573 The renderer function called after ImGui::Render() will receive that same value that the user code passed:
574
575 // Cast ImTextureID / void* stored in the draw command as our texture type
576 MyTexture* texture = (MyTexture*)pcmd->TextureId;
577 MyEngineBindTexture2D(texture);
578
579 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.
580 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.
581 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.
582
583 Here's a simplified OpenGL example using stb_image.h:
584
585 // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data:
586 #define STB_IMAGE_IMPLEMENTATION
587 #include <stb_image.h>
588 [...]
589 int my_image_width, my_image_height;
590 unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4);
591
592 // Turn the RGBA pixel data into an OpenGL texture:
593 GLuint my_opengl_texture;
594 glGenTextures(1, &my_opengl_texture);
595 glBindTexture(GL_TEXTURE_2D, my_opengl_texture);
596 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
597 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
598
599 // 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:
600 ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height));
601
602 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.
603 Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*.
604 Examples:
605
606 GLuint my_tex = XXX;
607 void* my_void_ptr;
608 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)
609 my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint
610
611 ID3D11ShaderResourceView* my_dx11_srv = XXX;
612 void* my_void_ptr;
613 my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void*
614 my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView*
615
616 Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated.
617
618 Q: How can I have multiple widgets with the same label or without a label?
619 Q: I have multiple widgets with the same label, and only the first one works. Why is that?
620 A: A primer on labels and the ID Stack...
621
622 Dear ImGui internally need to uniquely identify UI elements.
623 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID.
624 Interactive widgets (such as calls to Button buttons) need a unique ID.
625 Unique ID are used internally to track active widgets and occasionally associate state to widgets.
626 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element.
627
628 - Unique ID are often derived from a string label:
629
630 Button("OK"); // Label = "OK", ID = hash of (..., "OK")
631 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel")
632
633 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having
634 two buttons labeled "OK" in different windows or different tree locations is fine.
635 We used "..." above to signify whatever was already pushed to the ID stack previously:
636
637 Begin("MyWindow");
638 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK")
639 End();
640
641 - If you have a same ID twice in the same location, you'll have a conflict:
642
643 Button("OK");
644 Button("OK"); // ID collision! Interacting with either button will trigger the first one.
645
646 Fear not! this is easy to solve and there are many ways to solve it!
647
648 - Solving ID conflict in a simple/local context:
649 When passing a label you can optionally specify extra ID information within string itself.
650 Use "##" to pass a complement to the ID that won't be visible to the end-user.
651 This helps solving the simple collision cases when you know e.g. at compilation time which items
652 are going to be created:
653
654 Begin("MyWindow");
655 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play")
656 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above
657 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above
658 End();
659
660 - If you want to completely hide the label, but still need an ID:
661
662 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label!
663
664 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows
665 you to animate labels. For example you may want to include varying information in a window title bar,
666 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
667
668 Button("Hello###ID"; // Label = "Hello", ID = hash of (..., "ID")
669 Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different
670
671 sprintf(buf, "My game (%f FPS)###MyGame", fps);
672 Begin(buf); // Variable label, ID = hash of "MyGame"
673
674 - Solving ID conflict in a more general manner:
675 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts
676 within the same window. This is the most convenient way of distinguishing ID when iterating and
677 creating many UI elements programmatically.
678 You can push a pointer, a string or an integer value into the ID stack.
679 Remember that ID are formed from the concatenation of _everything_ in the ID stack!
680
681 Begin("Window");
682 for (int i = 0; i < 100; i++)
683 {
684 PushID(i); // Push i to the id tack
685 Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click")
686 PopID();
687 }
688 for (int i = 0; i < 100; i++)
689 {
690 MyObject* obj = Objects[i];
691 PushID(obj);
692 Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click")
693 PopID();
694 }
695 for (int i = 0; i < 100; i++)
696 {
697 MyObject* obj = Objects[i];
698 PushID(obj->Name);
699 Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click")
700 PopID();
701 }
702 End();
703
704 - More example showing that you can stack multiple prefixes into the ID stack:
705
706 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
707 PushID("node");
708 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
709 PushID(my_ptr);
710 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click")
711 PopID();
712 PopID();
713
714 - Tree nodes implicitly creates a scope for you by calling PushID().
715
716 Button("Click"); // Label = "Click", ID = hash of (..., "Click")
717 if (TreeNode("node"))
718 {
719 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click")
720 TreePop();
721 }
722
723 - When working with trees, ID are used to preserve the open/close state of each tree node.
724 Depending on your use cases you may want to use strings, indices or pointers as ID.
725 e.g. when following a single pointer that may change over time, using a static string as ID
726 will preserve your node open/closed state when the targeted object change.
727 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the
728 node open/closed state differently. See what makes more sense in your situation!
729
730 Q: How can I use my own math types instead of ImVec2/ImVec4?
731 A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions.
732 This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2.
733
734 Q: How can I load a different font than the default?
735 A: Use the font atlas to load the TTF/OTF file you want:
736 ImGuiIO& io = ImGui::GetIO();
737 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
738 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
739 (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code)
740
741 New programmers: remember that in C/C++ and most programming languages if you want to use a
742 backslash \ within a string literal, you need to write it double backslash "\\":
743 io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!)
744 io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT
745 io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT
746
747 Q: How can I easily use icons in my application?
748 A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you
749 main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?'
750 and the file 'misc/fonts/README.txt' for instructions and useful header files.
751
752 Q: How can I load multiple fonts?
753 A: Use the font atlas to pack them into a single texture:
754 (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.)
755
756 ImGuiIO& io = ImGui::GetIO();
757 ImFont* font0 = io.Fonts->AddFontDefault();
758 ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels);
759 ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels);
760 io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
761 // the first loaded font gets used by default
762 // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
763
764 // Options
765 ImFontConfig config;
766 config.OversampleH = 3;
767 config.OversampleV = 1;
768 config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up
769 config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters
770 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
771
772 // Combine multiple fonts into one (e.g. for icon fonts)
773 ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
774 ImFontConfig config;
775 config.MergeMode = true;
776 io.Fonts->AddFontDefault();
777 io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font
778 io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs
779
780 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
781 A: When loading a font, pass custom Unicode ranges to specify the glyphs to load.
782
783 // Add default Japanese ranges
784 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese());
785
786 // 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)
787 ImVector<ImWchar> ranges;
788 ImFontAtlas::GlyphRangesBuilder builder;
789 builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters)
790 builder.AddChar(0x7262); // Add a specific character
791 builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges
792 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
793 io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data);
794
795 All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8
796 by using the u8"hello" syntax. Specifying literal in your source code using a local code page
797 (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work!
798 Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8.
799
800 Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter().
801 The applications in examples/ are doing that.
802 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).
803 You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state.
804 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
805 the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly.
806
807 Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API)
808 A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags.
809 Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like.
810 - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows.
811 - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData.
812
813 Q: I integrated Dear ImGui in my engine and the text or lines are blurry..
814 A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f).
815 Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension.
816
817 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around..
818 A: You are probably mishandling the clipping rectangles in your render function.
819 Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height).
820
821 Q: How can I help?
822 A: - If you are experienced with Dear ImGui and C++, look at the github issues, or docs/TODO.txt and see how you want/can help!
823 - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README.
824 - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
825 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers.
826 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions.
827 - 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).
828
829 - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window.
830 this is also useful to set yourself in the context of another window (to get/set other settings)
831 - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug".
832 - 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
833 of a deep nested inner loop in your code.
834 - tip: you can call Render() multiple times (e.g for VR renders).
835 - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui!
836
837 */
838
839 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
840 #define _CRT_SECURE_NO_WARNINGS
841 #endif
842
843 #include "imgui.h"
844 #ifndef IMGUI_DEFINE_MATH_OPERATORS
845 #define IMGUI_DEFINE_MATH_OPERATORS
846 #endif
847 #include "imgui_internal.h"
848
849 #include <ctype.h> // toupper, isprint
850 #include <stdio.h> // vsnprintf, sscanf, printf
851 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
852 #include <stddef.h> // intptr_t
853 #else
854 #include <stdint.h> // intptr_t
855 #endif
856
857 #define IMGUI_DEBUG_NAV_SCORING 0
858 #define IMGUI_DEBUG_NAV_RECTS 0
859
860 // Visual Studio warnings
861 #ifdef _MSC_VER
862 #pragma warning (disable: 4127) // condition expression is constant
863 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
864 #endif
865
866 // Clang/GCC warnings with -Weverything
867 #ifdef __clang__
868 #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!
869 #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
870 #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.
871 #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.
872 #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.
873 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
874 #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
875 #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.
876 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int'
877 #elif defined(__GNUC__)
878 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
879 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
880 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'
881 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
882 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
883 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
884 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
885 #if __GNUC__ >= 8
886 #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
887 #endif
888 #endif
889
890 // 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.
891 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in
892 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear
893
894 //-------------------------------------------------------------------------
895 // [SECTION] FORWARD DECLARATIONS
896 //-------------------------------------------------------------------------
897
898 static void SetCurrentWindow(ImGuiWindow* window);
899 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
900 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
901 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
902 static void FindHoveredWindow();
903 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
904 static void CheckStacksSize(ImGuiWindow* window, bool write);
905 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges);
906
907 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
908 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_list, ImGuiWindow* window);
909 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
910
911 static ImRect GetViewportRect();
912
913 // Settings
914 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
915 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
916 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf);
917
918 // Platform Dependents default implementation for IO functions
919 static const char* GetClipboardTextFn_DefaultImpl(void* user_data);
920 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text);
921 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y);
922
923 namespace ImGui
924 {
925 static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags);
926
927 // Navigation
928 static void NavUpdate();
929 static void NavUpdateWindowing();
930 static void NavUpdateWindowingList();
931 static void NavUpdateMoveResult();
932 static float NavUpdatePageUpPageDown(int allowed_dir_flags);
933 static inline void NavUpdateAnyRequestFlag();
934 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id);
935 static ImVec2 NavCalcPreferredRefPos();
936 static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window);
937 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window);
938
939 // Misc
940 static void UpdateMouseInputs();
941 static void UpdateMouseWheel();
942 static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]);
943 }
944
945 //-----------------------------------------------------------------------------
946 // [SECTION] CONTEXT AND MEMORY ALLOCATORS
947 //-----------------------------------------------------------------------------
948
949 // Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL.
950 // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext().
951 // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file.
952 // 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:
953 // - 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
954 // - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts)
955 #ifndef GImGui
956 ImGuiContext* GImGui = NULL;
957 #endif
958
959 // Memory Allocator functions. Use SetAllocatorFunctions() to change them.
960 // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file.
961 // 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.
962 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
MallocWrapper(size_t size,void * user_data)963 static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size); }
FreeWrapper(void * ptr,void * user_data)964 static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr); }
965 #else
MallocWrapper(size_t size,void * user_data)966 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)967 static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); }
968 #endif
969
970 static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
971 static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
972 static void* GImAllocatorUserData = NULL;
973
974 //-----------------------------------------------------------------------------
975 // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)
976 //-----------------------------------------------------------------------------
977
ImGuiStyle()978 ImGuiStyle::ImGuiStyle()
979 {
980 Alpha = 1.0f; // Global alpha applies to everything in ImGui
981 WindowPadding = ImVec2(8,8); // Padding within a window
982 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows
983 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
984 WindowMinSize = ImVec2(32,32); // Minimum window size
985 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text
986 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
987 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
988 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
989 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
990 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets)
991 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
992 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
993 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines
994 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
995 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!
996 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
997 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns
998 ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar
999 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar
1000 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar
1001 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1002 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1003 DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows.
1004 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.
1005 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1006 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU.
1007 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.)
1008 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.
1009
1010 // Default theme
1011 ImGui::StyleColorsDark(this);
1012 }
1013
1014 // 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.
1015 // 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)1016 void ImGuiStyle::ScaleAllSizes(float scale_factor)
1017 {
1018 WindowPadding = ImFloor(WindowPadding * scale_factor);
1019 WindowRounding = ImFloor(WindowRounding * scale_factor);
1020 WindowMinSize = ImFloor(WindowMinSize * scale_factor);
1021 ChildRounding = ImFloor(ChildRounding * scale_factor);
1022 PopupRounding = ImFloor(PopupRounding * scale_factor);
1023 FramePadding = ImFloor(FramePadding * scale_factor);
1024 FrameRounding = ImFloor(FrameRounding * scale_factor);
1025 ItemSpacing = ImFloor(ItemSpacing * scale_factor);
1026 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor);
1027 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor);
1028 IndentSpacing = ImFloor(IndentSpacing * scale_factor);
1029 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor);
1030 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor);
1031 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor);
1032 GrabMinSize = ImFloor(GrabMinSize * scale_factor);
1033 GrabRounding = ImFloor(GrabRounding * scale_factor);
1034 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor);
1035 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor);
1036 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor);
1037 }
1038
ImGuiIO()1039 ImGuiIO::ImGuiIO()
1040 {
1041 // Most fields are initialized with zero
1042 memset(this, 0, sizeof(*this));
1043
1044 // Settings
1045 ConfigFlags = 0x00;
1046 BackendFlags = 0x00;
1047 DisplaySize = ImVec2(-1.0f, -1.0f);
1048 DeltaTime = 1.0f/60.0f;
1049 IniSavingRate = 5.0f;
1050 IniFilename = "imgui.ini";
1051 LogFilename = "imgui_log.txt";
1052 MouseDoubleClickTime = 0.30f;
1053 MouseDoubleClickMaxDist = 6.0f;
1054 for (int i = 0; i < ImGuiKey_COUNT; i++)
1055 KeyMap[i] = -1;
1056 KeyRepeatDelay = 0.250f;
1057 KeyRepeatRate = 0.050f;
1058 UserData = NULL;
1059
1060 Fonts = NULL;
1061 FontGlobalScale = 1.0f;
1062 FontDefault = NULL;
1063 FontAllowUserScaling = false;
1064 DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1065 DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
1066
1067 // Miscellaneous configuration options
1068 #ifdef __APPLE__
1069 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
1070 #else
1071 ConfigMacOSXBehaviors = false;
1072 #endif
1073 ConfigInputTextCursorBlink = true;
1074 ConfigResizeWindowsFromEdges = false;
1075
1076 // Settings (User Functions)
1077 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations
1078 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;
1079 ClipboardUserData = NULL;
1080 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl;
1081 ImeWindowHandle = NULL;
1082
1083 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1084 RenderDrawListsFn = NULL;
1085 #endif
1086
1087 // Input (NB: we already have memset zero the entire structure)
1088 MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1089 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1090 MouseDragThreshold = 6.0f;
1091 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1092 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f;
1093 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
1094 }
1095
1096 // Pass in translated ASCII characters for text input.
1097 // - with glfw you can get those from the callback set in glfwSetCharCallback()
1098 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
AddInputCharacter(ImWchar c)1099 void ImGuiIO::AddInputCharacter(ImWchar c)
1100 {
1101 const int n = ImStrlenW(InputCharacters);
1102 if (n + 1 < IM_ARRAYSIZE(InputCharacters))
1103 {
1104 InputCharacters[n] = c;
1105 InputCharacters[n+1] = '\0';
1106 }
1107 }
1108
AddInputCharactersUTF8(const char * utf8_chars)1109 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1110 {
1111 // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more
1112 const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar);
1113 ImWchar wchars[wchars_buf_len];
1114 ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL);
1115 for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++)
1116 AddInputCharacter(wchars[i]);
1117 }
1118
1119 //-----------------------------------------------------------------------------
1120 // [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions)
1121 //-----------------------------------------------------------------------------
1122
ImLineClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & p)1123 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
1124 {
1125 ImVec2 ap = p - a;
1126 ImVec2 ab_dir = b - a;
1127 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
1128 if (dot < 0.0f)
1129 return a;
1130 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
1131 if (dot > ab_len_sqr)
1132 return b;
1133 return a + ab_dir * dot / ab_len_sqr;
1134 }
1135
ImTriangleContainsPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1136 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1137 {
1138 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
1139 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
1140 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
1141 return ((b1 == b2) && (b2 == b3));
1142 }
1143
ImTriangleBarycentricCoords(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p,float & out_u,float & out_v,float & out_w)1144 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
1145 {
1146 ImVec2 v0 = b - a;
1147 ImVec2 v1 = c - a;
1148 ImVec2 v2 = p - a;
1149 const float denom = v0.x * v1.y - v1.x * v0.y;
1150 out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
1151 out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
1152 out_u = 1.0f - out_v - out_w;
1153 }
1154
ImTriangleClosestPoint(const ImVec2 & a,const ImVec2 & b,const ImVec2 & c,const ImVec2 & p)1155 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
1156 {
1157 ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
1158 ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
1159 ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
1160 float dist2_ab = ImLengthSqr(p - proj_ab);
1161 float dist2_bc = ImLengthSqr(p - proj_bc);
1162 float dist2_ca = ImLengthSqr(p - proj_ca);
1163 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
1164 if (m == dist2_ab)
1165 return proj_ab;
1166 if (m == dist2_bc)
1167 return proj_bc;
1168 return proj_ca;
1169 }
1170
ImStricmp(const char * str1,const char * str2)1171 int ImStricmp(const char* str1, const char* str2)
1172 {
1173 int d;
1174 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; }
1175 return d;
1176 }
1177
ImStrnicmp(const char * str1,const char * str2,size_t count)1178 int ImStrnicmp(const char* str1, const char* str2, size_t count)
1179 {
1180 int d = 0;
1181 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
1182 return d;
1183 }
1184
ImStrncpy(char * dst,const char * src,size_t count)1185 void ImStrncpy(char* dst, const char* src, size_t count)
1186 {
1187 if (count < 1) return;
1188 strncpy(dst, src, count);
1189 dst[count-1] = 0;
1190 }
1191
ImStrdup(const char * str)1192 char* ImStrdup(const char *str)
1193 {
1194 size_t len = strlen(str) + 1;
1195 void* buf = ImGui::MemAlloc(len);
1196 return (char*)memcpy(buf, (const void*)str, len);
1197 }
1198
ImStrchrRange(const char * str,const char * str_end,char c)1199 const char* ImStrchrRange(const char* str, const char* str_end, char c)
1200 {
1201 for ( ; str < str_end; str++)
1202 if (*str == c)
1203 return str;
1204 return NULL;
1205 }
1206
ImStrlenW(const ImWchar * str)1207 int ImStrlenW(const ImWchar* str)
1208 {
1209 int n = 0;
1210 while (*str++) n++;
1211 return n;
1212 }
1213
ImStrbolW(const ImWchar * buf_mid_line,const ImWchar * buf_begin)1214 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line
1215 {
1216 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
1217 buf_mid_line--;
1218 return buf_mid_line;
1219 }
1220
ImStristr(const char * haystack,const char * haystack_end,const char * needle,const char * needle_end)1221 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
1222 {
1223 if (!needle_end)
1224 needle_end = needle + strlen(needle);
1225
1226 const char un0 = (char)toupper(*needle);
1227 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
1228 {
1229 if (toupper(*haystack) == un0)
1230 {
1231 const char* b = needle + 1;
1232 for (const char* a = haystack + 1; b < needle_end; a++, b++)
1233 if (toupper(*a) != toupper(*b))
1234 break;
1235 if (b == needle_end)
1236 return haystack;
1237 }
1238 haystack++;
1239 }
1240 return NULL;
1241 }
1242
1243 // 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)1244 void ImStrTrimBlanks(char* buf)
1245 {
1246 char* p = buf;
1247 while (p[0] == ' ' || p[0] == '\t') // Leading blanks
1248 p++;
1249 char* p_start = p;
1250 while (*p != 0) // Find end of string
1251 p++;
1252 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks
1253 p--;
1254 if (p_start != buf) // Copy memory if we had leading blanks
1255 memmove(buf, p_start, p - p_start);
1256 buf[p - p_start] = 0; // Zero terminate
1257 }
1258
1259 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
1260 // 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.
1261 // B) When buf==NULL vsnprintf() will return the output size.
1262 #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1263
1264 #if defined(_MSC_VER) && !defined(vsnprintf)
1265 #define vsnprintf _vsnprintf
1266 #endif
1267
ImFormatString(char * buf,size_t buf_size,const char * fmt,...)1268 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
1269 {
1270 va_list args;
1271 va_start(args, fmt);
1272 int w = vsnprintf(buf, buf_size, fmt, args);
1273 va_end(args);
1274 if (buf == NULL)
1275 return w;
1276 if (w == -1 || w >= (int)buf_size)
1277 w = (int)buf_size - 1;
1278 buf[w] = 0;
1279 return w;
1280 }
1281
ImFormatStringV(char * buf,size_t buf_size,const char * fmt,va_list args)1282 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
1283 {
1284 int w = vsnprintf(buf, buf_size, fmt, args);
1285 if (buf == NULL)
1286 return w;
1287 if (w == -1 || w >= (int)buf_size)
1288 w = (int)buf_size - 1;
1289 buf[w] = 0;
1290 return w;
1291 }
1292 #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS
1293
1294 // Pass data_size==0 for zero-terminated strings
1295 // 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)1296 ImU32 ImHash(const void* data, int data_size, ImU32 seed)
1297 {
1298 static ImU32 crc32_lut[256] = { 0 };
1299 if (!crc32_lut[1])
1300 {
1301 const ImU32 polynomial = 0xEDB88320;
1302 for (ImU32 i = 0; i < 256; i++)
1303 {
1304 ImU32 crc = i;
1305 for (ImU32 j = 0; j < 8; j++)
1306 crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial);
1307 crc32_lut[i] = crc;
1308 }
1309 }
1310
1311 seed = ~seed;
1312 ImU32 crc = seed;
1313 const unsigned char* current = (const unsigned char*)data;
1314
1315 if (data_size > 0)
1316 {
1317 // Known size
1318 while (data_size--)
1319 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++];
1320 }
1321 else
1322 {
1323 // Zero-terminated string
1324 while (unsigned char c = *current++)
1325 {
1326 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
1327 // Because this syntax is rarely used we are optimizing for the common case.
1328 // - If we reach ### in the string we discard the hash so far and reset to the seed.
1329 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
1330 if (c == '#' && current[0] == '#' && current[1] == '#')
1331 crc = seed;
1332 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
1333 }
1334 }
1335 return ~crc;
1336 }
1337
ImFileOpen(const char * filename,const char * mode)1338 FILE* ImFileOpen(const char* filename, const char* mode)
1339 {
1340 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__)
1341 // 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)
1342 const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1;
1343 const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1;
1344 ImVector<ImWchar> buf;
1345 buf.resize(filename_wsize + mode_wsize);
1346 ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL);
1347 ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL);
1348 return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]);
1349 #else
1350 return fopen(filename, mode);
1351 #endif
1352 }
1353
1354 // Load file content into memory
1355 // 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)1356 void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes)
1357 {
1358 IM_ASSERT(filename && file_open_mode);
1359 if (out_file_size)
1360 *out_file_size = 0;
1361
1362 FILE* f;
1363 if ((f = ImFileOpen(filename, file_open_mode)) == NULL)
1364 return NULL;
1365
1366 long file_size_signed;
1367 if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET))
1368 {
1369 fclose(f);
1370 return NULL;
1371 }
1372
1373 size_t file_size = (size_t)file_size_signed;
1374 void* file_data = ImGui::MemAlloc(file_size + padding_bytes);
1375 if (file_data == NULL)
1376 {
1377 fclose(f);
1378 return NULL;
1379 }
1380 if (fread(file_data, 1, file_size, f) != file_size)
1381 {
1382 fclose(f);
1383 ImGui::MemFree(file_data);
1384 return NULL;
1385 }
1386 if (padding_bytes > 0)
1387 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
1388
1389 fclose(f);
1390 if (out_file_size)
1391 *out_file_size = file_size;
1392
1393 return file_data;
1394 }
1395
1396 //-----------------------------------------------------------------------------
1397 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
1398 //-----------------------------------------------------------------------------
1399
1400 // Convert UTF-8 to 32-bits character, process single character input.
1401 // Based on stb_from_utf8() from github.com/nothings/stb/
1402 // We handle UTF-8 decoding error by skipping forward.
ImTextCharFromUtf8(unsigned int * out_char,const char * in_text,const char * in_text_end)1403 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
1404 {
1405 unsigned int c = (unsigned int)-1;
1406 const unsigned char* str = (const unsigned char*)in_text;
1407 if (!(*str & 0x80))
1408 {
1409 c = (unsigned int)(*str++);
1410 *out_char = c;
1411 return 1;
1412 }
1413 if ((*str & 0xe0) == 0xc0)
1414 {
1415 *out_char = 0xFFFD; // will be invalid but not end of string
1416 if (in_text_end && in_text_end - (const char*)str < 2) return 1;
1417 if (*str < 0xc2) return 2;
1418 c = (unsigned int)((*str++ & 0x1f) << 6);
1419 if ((*str & 0xc0) != 0x80) return 2;
1420 c += (*str++ & 0x3f);
1421 *out_char = c;
1422 return 2;
1423 }
1424 if ((*str & 0xf0) == 0xe0)
1425 {
1426 *out_char = 0xFFFD; // will be invalid but not end of string
1427 if (in_text_end && in_text_end - (const char*)str < 3) return 1;
1428 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3;
1429 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below
1430 c = (unsigned int)((*str++ & 0x0f) << 12);
1431 if ((*str & 0xc0) != 0x80) return 3;
1432 c += (unsigned int)((*str++ & 0x3f) << 6);
1433 if ((*str & 0xc0) != 0x80) return 3;
1434 c += (*str++ & 0x3f);
1435 *out_char = c;
1436 return 3;
1437 }
1438 if ((*str & 0xf8) == 0xf0)
1439 {
1440 *out_char = 0xFFFD; // will be invalid but not end of string
1441 if (in_text_end && in_text_end - (const char*)str < 4) return 1;
1442 if (*str > 0xf4) return 4;
1443 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4;
1444 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below
1445 c = (unsigned int)((*str++ & 0x07) << 18);
1446 if ((*str & 0xc0) != 0x80) return 4;
1447 c += (unsigned int)((*str++ & 0x3f) << 12);
1448 if ((*str & 0xc0) != 0x80) return 4;
1449 c += (unsigned int)((*str++ & 0x3f) << 6);
1450 if ((*str & 0xc0) != 0x80) return 4;
1451 c += (*str++ & 0x3f);
1452 // utf-8 encodings of values used in surrogate pairs are invalid
1453 if ((c & 0xFFFFF800) == 0xD800) return 4;
1454 *out_char = c;
1455 return 4;
1456 }
1457 *out_char = 0;
1458 return 0;
1459 }
1460
ImTextStrFromUtf8(ImWchar * buf,int buf_size,const char * in_text,const char * in_text_end,const char ** in_text_remaining)1461 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
1462 {
1463 ImWchar* buf_out = buf;
1464 ImWchar* buf_end = buf + buf_size;
1465 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1466 {
1467 unsigned int c;
1468 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1469 if (c == 0)
1470 break;
1471 if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes
1472 *buf_out++ = (ImWchar)c;
1473 }
1474 *buf_out = 0;
1475 if (in_text_remaining)
1476 *in_text_remaining = in_text;
1477 return (int)(buf_out - buf);
1478 }
1479
ImTextCountCharsFromUtf8(const char * in_text,const char * in_text_end)1480 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
1481 {
1482 int char_count = 0;
1483 while ((!in_text_end || in_text < in_text_end) && *in_text)
1484 {
1485 unsigned int c;
1486 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
1487 if (c == 0)
1488 break;
1489 if (c < 0x10000)
1490 char_count++;
1491 }
1492 return char_count;
1493 }
1494
1495 // Based on stb_to_utf8() from github.com/nothings/stb/
ImTextCharToUtf8(char * buf,int buf_size,unsigned int c)1496 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c)
1497 {
1498 if (c < 0x80)
1499 {
1500 buf[0] = (char)c;
1501 return 1;
1502 }
1503 if (c < 0x800)
1504 {
1505 if (buf_size < 2) return 0;
1506 buf[0] = (char)(0xc0 + (c >> 6));
1507 buf[1] = (char)(0x80 + (c & 0x3f));
1508 return 2;
1509 }
1510 if (c >= 0xdc00 && c < 0xe000)
1511 {
1512 return 0;
1513 }
1514 if (c >= 0xd800 && c < 0xdc00)
1515 {
1516 if (buf_size < 4) return 0;
1517 buf[0] = (char)(0xf0 + (c >> 18));
1518 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
1519 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
1520 buf[3] = (char)(0x80 + ((c ) & 0x3f));
1521 return 4;
1522 }
1523 //else if (c < 0x10000)
1524 {
1525 if (buf_size < 3) return 0;
1526 buf[0] = (char)(0xe0 + (c >> 12));
1527 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f));
1528 buf[2] = (char)(0x80 + ((c ) & 0x3f));
1529 return 3;
1530 }
1531 }
1532
1533 // Not optimal but we very rarely use this function.
ImTextCountUtf8BytesFromChar(const char * in_text,const char * in_text_end)1534 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
1535 {
1536 unsigned int dummy = 0;
1537 return ImTextCharFromUtf8(&dummy, in_text, in_text_end);
1538 }
1539
ImTextCountUtf8BytesFromChar(unsigned int c)1540 static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
1541 {
1542 if (c < 0x80) return 1;
1543 if (c < 0x800) return 2;
1544 if (c >= 0xdc00 && c < 0xe000) return 0;
1545 if (c >= 0xd800 && c < 0xdc00) return 4;
1546 return 3;
1547 }
1548
ImTextStrToUtf8(char * buf,int buf_size,const ImWchar * in_text,const ImWchar * in_text_end)1549 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
1550 {
1551 char* buf_out = buf;
1552 const char* buf_end = buf + buf_size;
1553 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text)
1554 {
1555 unsigned int c = (unsigned int)(*in_text++);
1556 if (c < 0x80)
1557 *buf_out++ = (char)c;
1558 else
1559 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c);
1560 }
1561 *buf_out = 0;
1562 return (int)(buf_out - buf);
1563 }
1564
ImTextCountUtf8BytesFromStr(const ImWchar * in_text,const ImWchar * in_text_end)1565 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
1566 {
1567 int bytes_count = 0;
1568 while ((!in_text_end || in_text < in_text_end) && *in_text)
1569 {
1570 unsigned int c = (unsigned int)(*in_text++);
1571 if (c < 0x80)
1572 bytes_count++;
1573 else
1574 bytes_count += ImTextCountUtf8BytesFromChar(c);
1575 }
1576 return bytes_count;
1577 }
1578
1579 //-----------------------------------------------------------------------------
1580 // [SECTION] MISC HELPER/UTILTIES (Color functions)
1581 // Note: The Convert functions are early design which are not consistent with other API.
1582 //-----------------------------------------------------------------------------
1583
ColorConvertU32ToFloat4(ImU32 in)1584 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
1585 {
1586 float s = 1.0f/255.0f;
1587 return ImVec4(
1588 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
1589 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
1590 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
1591 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
1592 }
1593
ColorConvertFloat4ToU32(const ImVec4 & in)1594 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
1595 {
1596 ImU32 out;
1597 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
1598 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
1599 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
1600 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
1601 return out;
1602 }
1603
1604 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
1605 // 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)1606 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
1607 {
1608 float K = 0.f;
1609 if (g < b)
1610 {
1611 ImSwap(g, b);
1612 K = -1.f;
1613 }
1614 if (r < g)
1615 {
1616 ImSwap(r, g);
1617 K = -2.f / 6.f - K;
1618 }
1619
1620 const float chroma = r - (g < b ? g : b);
1621 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
1622 out_s = chroma / (r + 1e-20f);
1623 out_v = r;
1624 }
1625
1626 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
1627 // 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)1628 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
1629 {
1630 if (s == 0.0f)
1631 {
1632 // gray
1633 out_r = out_g = out_b = v;
1634 return;
1635 }
1636
1637 h = ImFmod(h, 1.0f) / (60.0f/360.0f);
1638 int i = (int)h;
1639 float f = h - (float)i;
1640 float p = v * (1.0f - s);
1641 float q = v * (1.0f - s * f);
1642 float t = v * (1.0f - s * (1.0f - f));
1643
1644 switch (i)
1645 {
1646 case 0: out_r = v; out_g = t; out_b = p; break;
1647 case 1: out_r = q; out_g = v; out_b = p; break;
1648 case 2: out_r = p; out_g = v; out_b = t; break;
1649 case 3: out_r = p; out_g = q; out_b = v; break;
1650 case 4: out_r = t; out_g = p; out_b = v; break;
1651 case 5: default: out_r = v; out_g = p; out_b = q; break;
1652 }
1653 }
1654
GetColorU32(ImGuiCol idx,float alpha_mul)1655 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
1656 {
1657 ImGuiStyle& style = GImGui->Style;
1658 ImVec4 c = style.Colors[idx];
1659 c.w *= style.Alpha * alpha_mul;
1660 return ColorConvertFloat4ToU32(c);
1661 }
1662
GetColorU32(const ImVec4 & col)1663 ImU32 ImGui::GetColorU32(const ImVec4& col)
1664 {
1665 ImGuiStyle& style = GImGui->Style;
1666 ImVec4 c = col;
1667 c.w *= style.Alpha;
1668 return ColorConvertFloat4ToU32(c);
1669 }
1670
GetStyleColorVec4(ImGuiCol idx)1671 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
1672 {
1673 ImGuiStyle& style = GImGui->Style;
1674 return style.Colors[idx];
1675 }
1676
GetColorU32(ImU32 col)1677 ImU32 ImGui::GetColorU32(ImU32 col)
1678 {
1679 float style_alpha = GImGui->Style.Alpha;
1680 if (style_alpha >= 1.0f)
1681 return col;
1682 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
1683 a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.
1684 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
1685 }
1686
1687 //-----------------------------------------------------------------------------
1688 // [SECTION] ImGuiStorage
1689 // Helper: Key->value storage
1690 //-----------------------------------------------------------------------------
1691
1692 // std::lower_bound but without the bullshit
LowerBound(ImVector<ImGuiStorage::Pair> & data,ImGuiID key)1693 static ImVector<ImGuiStorage::Pair>::iterator LowerBound(ImVector<ImGuiStorage::Pair>& data, ImGuiID key)
1694 {
1695 ImVector<ImGuiStorage::Pair>::iterator first = data.begin();
1696 ImVector<ImGuiStorage::Pair>::iterator last = data.end();
1697 size_t count = (size_t)(last - first);
1698 while (count > 0)
1699 {
1700 size_t count2 = count >> 1;
1701 ImVector<ImGuiStorage::Pair>::iterator mid = first + count2;
1702 if (mid->key < key)
1703 {
1704 first = ++mid;
1705 count -= count2 + 1;
1706 }
1707 else
1708 {
1709 count = count2;
1710 }
1711 }
1712 return first;
1713 }
1714
1715 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
BuildSortByKey()1716 void ImGuiStorage::BuildSortByKey()
1717 {
1718 struct StaticFunc
1719 {
1720 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs)
1721 {
1722 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
1723 if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1;
1724 if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1;
1725 return 0;
1726 }
1727 };
1728 if (Data.Size > 1)
1729 ImQsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID);
1730 }
1731
GetInt(ImGuiID key,int default_val) const1732 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
1733 {
1734 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1735 if (it == Data.end() || it->key != key)
1736 return default_val;
1737 return it->val_i;
1738 }
1739
GetBool(ImGuiID key,bool default_val) const1740 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
1741 {
1742 return GetInt(key, default_val ? 1 : 0) != 0;
1743 }
1744
GetFloat(ImGuiID key,float default_val) const1745 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
1746 {
1747 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1748 if (it == Data.end() || it->key != key)
1749 return default_val;
1750 return it->val_f;
1751 }
1752
GetVoidPtr(ImGuiID key) const1753 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
1754 {
1755 ImVector<Pair>::iterator it = LowerBound(const_cast<ImVector<ImGuiStorage::Pair>&>(Data), key);
1756 if (it == Data.end() || it->key != key)
1757 return NULL;
1758 return it->val_p;
1759 }
1760
1761 // 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)1762 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
1763 {
1764 ImVector<Pair>::iterator it = LowerBound(Data, key);
1765 if (it == Data.end() || it->key != key)
1766 it = Data.insert(it, Pair(key, default_val));
1767 return &it->val_i;
1768 }
1769
GetBoolRef(ImGuiID key,bool default_val)1770 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
1771 {
1772 return (bool*)GetIntRef(key, default_val ? 1 : 0);
1773 }
1774
GetFloatRef(ImGuiID key,float default_val)1775 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
1776 {
1777 ImVector<Pair>::iterator it = LowerBound(Data, key);
1778 if (it == Data.end() || it->key != key)
1779 it = Data.insert(it, Pair(key, default_val));
1780 return &it->val_f;
1781 }
1782
GetVoidPtrRef(ImGuiID key,void * default_val)1783 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
1784 {
1785 ImVector<Pair>::iterator it = LowerBound(Data, key);
1786 if (it == Data.end() || it->key != key)
1787 it = Data.insert(it, Pair(key, default_val));
1788 return &it->val_p;
1789 }
1790
1791 // 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)1792 void ImGuiStorage::SetInt(ImGuiID key, int val)
1793 {
1794 ImVector<Pair>::iterator it = LowerBound(Data, key);
1795 if (it == Data.end() || it->key != key)
1796 {
1797 Data.insert(it, Pair(key, val));
1798 return;
1799 }
1800 it->val_i = val;
1801 }
1802
SetBool(ImGuiID key,bool val)1803 void ImGuiStorage::SetBool(ImGuiID key, bool val)
1804 {
1805 SetInt(key, val ? 1 : 0);
1806 }
1807
SetFloat(ImGuiID key,float val)1808 void ImGuiStorage::SetFloat(ImGuiID key, float val)
1809 {
1810 ImVector<Pair>::iterator it = LowerBound(Data, key);
1811 if (it == Data.end() || it->key != key)
1812 {
1813 Data.insert(it, Pair(key, val));
1814 return;
1815 }
1816 it->val_f = val;
1817 }
1818
SetVoidPtr(ImGuiID key,void * val)1819 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
1820 {
1821 ImVector<Pair>::iterator it = LowerBound(Data, key);
1822 if (it == Data.end() || it->key != key)
1823 {
1824 Data.insert(it, Pair(key, val));
1825 return;
1826 }
1827 it->val_p = val;
1828 }
1829
SetAllInt(int v)1830 void ImGuiStorage::SetAllInt(int v)
1831 {
1832 for (int i = 0; i < Data.Size; i++)
1833 Data[i].val_i = v;
1834 }
1835
1836 //-----------------------------------------------------------------------------
1837 // [SECTION] ImGuiTextFilter
1838 //-----------------------------------------------------------------------------
1839
1840 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
ImGuiTextFilter(const char * default_filter)1841 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter)
1842 {
1843 if (default_filter)
1844 {
1845 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
1846 Build();
1847 }
1848 else
1849 {
1850 InputBuf[0] = 0;
1851 CountGrep = 0;
1852 }
1853 }
1854
Draw(const char * label,float width)1855 bool ImGuiTextFilter::Draw(const char* label, float width)
1856 {
1857 if (width != 0.0f)
1858 ImGui::PushItemWidth(width);
1859 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
1860 if (width != 0.0f)
1861 ImGui::PopItemWidth();
1862 if (value_changed)
1863 Build();
1864 return value_changed;
1865 }
1866
split(char separator,ImVector<TextRange> * out) const1867 void ImGuiTextFilter::TextRange::split(char separator, ImVector<TextRange>* out) const
1868 {
1869 out->resize(0);
1870 const char* wb = b;
1871 const char* we = wb;
1872 while (we < e)
1873 {
1874 if (*we == separator)
1875 {
1876 out->push_back(TextRange(wb, we));
1877 wb = we + 1;
1878 }
1879 we++;
1880 }
1881 if (wb != we)
1882 out->push_back(TextRange(wb, we));
1883 }
1884
Build()1885 void ImGuiTextFilter::Build()
1886 {
1887 Filters.resize(0);
1888 TextRange input_range(InputBuf, InputBuf+strlen(InputBuf));
1889 input_range.split(',', &Filters);
1890
1891 CountGrep = 0;
1892 for (int i = 0; i != Filters.Size; i++)
1893 {
1894 TextRange& f = Filters[i];
1895 while (f.b < f.e && ImCharIsBlankA(f.b[0]))
1896 f.b++;
1897 while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
1898 f.e--;
1899 if (f.empty())
1900 continue;
1901 if (Filters[i].b[0] != '-')
1902 CountGrep += 1;
1903 }
1904 }
1905
PassFilter(const char * text,const char * text_end) const1906 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
1907 {
1908 if (Filters.empty())
1909 return true;
1910
1911 if (text == NULL)
1912 text = "";
1913
1914 for (int i = 0; i != Filters.Size; i++)
1915 {
1916 const TextRange& f = Filters[i];
1917 if (f.empty())
1918 continue;
1919 if (f.b[0] == '-')
1920 {
1921 // Subtract
1922 if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL)
1923 return false;
1924 }
1925 else
1926 {
1927 // Grep
1928 if (ImStristr(text, text_end, f.begin(), f.end()) != NULL)
1929 return true;
1930 }
1931 }
1932
1933 // Implicit * grep
1934 if (CountGrep == 0)
1935 return true;
1936
1937 return false;
1938 }
1939
1940 //-----------------------------------------------------------------------------
1941 // [SECTION] ImGuiTextBuffer
1942 //-----------------------------------------------------------------------------
1943
1944 // On some platform vsnprintf() takes va_list by reference and modifies it.
1945 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
1946 #ifndef va_copy
1947 #if defined(__GNUC__) || defined(__clang__)
1948 #define va_copy(dest, src) __builtin_va_copy(dest, src)
1949 #else
1950 #define va_copy(dest, src) (dest = src)
1951 #endif
1952 #endif
1953
1954 // Helper: Text buffer for logging/accumulating text
appendfv(const char * fmt,va_list args)1955 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
1956 {
1957 va_list args_copy;
1958 va_copy(args_copy, args);
1959
1960 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
1961 if (len <= 0)
1962 {
1963 va_end(args_copy);
1964 return;
1965 }
1966
1967 const int write_off = Buf.Size;
1968 const int needed_sz = write_off + len;
1969 if (write_off + len >= Buf.Capacity)
1970 {
1971 int double_capacity = Buf.Capacity * 2;
1972 Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity);
1973 }
1974
1975 Buf.resize(needed_sz);
1976 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
1977 va_end(args_copy);
1978 }
1979
appendf(const char * fmt,...)1980 void ImGuiTextBuffer::appendf(const char* fmt, ...)
1981 {
1982 va_list args;
1983 va_start(args, fmt);
1984 appendfv(fmt, args);
1985 va_end(args);
1986 }
1987
1988 //-----------------------------------------------------------------------------
1989 // [SECTION] ImGuiListClipper
1990 // This is currently not as flexible/powerful as it should be, needs some rework (see TODO)
1991 //-----------------------------------------------------------------------------
1992
SetCursorPosYAndSetupDummyPrevLine(float pos_y,float line_height)1993 static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height)
1994 {
1995 // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor.
1996 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
1997 // The clipper should probably have a 4th step to display the last item in a regular manner.
1998 ImGui::SetCursorPosY(pos_y);
1999 ImGuiWindow* window = ImGui::GetCurrentWindow();
2000 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage.
2001 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.
2002 if (window->DC.ColumnsSet)
2003 window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
2004 }
2005
2006 // Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
2007 // Use case B: Begin() called from constructor with items_height>0
2008 // 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)2009 void ImGuiListClipper::Begin(int count, float items_height)
2010 {
2011 StartPosY = ImGui::GetCursorPosY();
2012 ItemsHeight = items_height;
2013 ItemsCount = count;
2014 StepNo = 0;
2015 DisplayEnd = DisplayStart = -1;
2016 if (ItemsHeight > 0.0f)
2017 {
2018 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
2019 if (DisplayStart > 0)
2020 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
2021 StepNo = 2;
2022 }
2023 }
2024
End()2025 void ImGuiListClipper::End()
2026 {
2027 if (ItemsCount < 0)
2028 return;
2029 // 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.
2030 if (ItemsCount < INT_MAX)
2031 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
2032 ItemsCount = -1;
2033 StepNo = 3;
2034 }
2035
Step()2036 bool ImGuiListClipper::Step()
2037 {
2038 if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems)
2039 {
2040 ItemsCount = -1;
2041 return false;
2042 }
2043 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.
2044 {
2045 DisplayStart = 0;
2046 DisplayEnd = 1;
2047 StartPosY = ImGui::GetCursorPosY();
2048 StepNo = 1;
2049 return true;
2050 }
2051 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.
2052 {
2053 if (ItemsCount == 1) { ItemsCount = -1; return false; }
2054 float items_height = ImGui::GetCursorPosY() - StartPosY;
2055 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
2056 Begin(ItemsCount-1, items_height);
2057 DisplayStart++;
2058 DisplayEnd++;
2059 StepNo = 3;
2060 return true;
2061 }
2062 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.
2063 {
2064 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
2065 StepNo = 3;
2066 return true;
2067 }
2068 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.
2069 End();
2070 return false;
2071 }
2072
2073 //-----------------------------------------------------------------------------
2074 // [SECTION] RENDER HELPERS
2075 // Those (internal) functions are currently quite a legacy mess - their signature and behavior will change.
2076 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state.
2077 //-----------------------------------------------------------------------------
2078
FindRenderedTextEnd(const char * text,const char * text_end)2079 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
2080 {
2081 const char* text_display_end = text;
2082 if (!text_end)
2083 text_end = (const char*)-1;
2084
2085 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
2086 text_display_end++;
2087 return text_display_end;
2088 }
2089
2090 // Internal ImGui functions to render text
2091 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
RenderText(ImVec2 pos,const char * text,const char * text_end,bool hide_text_after_hash)2092 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
2093 {
2094 ImGuiContext& g = *GImGui;
2095 ImGuiWindow* window = g.CurrentWindow;
2096
2097 // Hide anything after a '##' string
2098 const char* text_display_end;
2099 if (hide_text_after_hash)
2100 {
2101 text_display_end = FindRenderedTextEnd(text, text_end);
2102 }
2103 else
2104 {
2105 if (!text_end)
2106 text_end = text + strlen(text); // FIXME-OPT
2107 text_display_end = text_end;
2108 }
2109
2110 if (text != text_display_end)
2111 {
2112 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
2113 if (g.LogEnabled)
2114 LogRenderedText(&pos, text, text_display_end);
2115 }
2116 }
2117
RenderTextWrapped(ImVec2 pos,const char * text,const char * text_end,float wrap_width)2118 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
2119 {
2120 ImGuiContext& g = *GImGui;
2121 ImGuiWindow* window = g.CurrentWindow;
2122
2123 if (!text_end)
2124 text_end = text + strlen(text); // FIXME-OPT
2125
2126 if (text != text_end)
2127 {
2128 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
2129 if (g.LogEnabled)
2130 LogRenderedText(&pos, text, text_end);
2131 }
2132 }
2133
2134 // Default clip_rect uses (pos_min,pos_max)
2135 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
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)2136 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)
2137 {
2138 // Hide anything after a '##' string
2139 const char* text_display_end = FindRenderedTextEnd(text, text_end);
2140 const int text_len = (int)(text_display_end - text);
2141 if (text_len == 0)
2142 return;
2143
2144 ImGuiContext& g = *GImGui;
2145 ImGuiWindow* window = g.CurrentWindow;
2146
2147 // Perform CPU side clipping for single clipped element to avoid using scissor state
2148 ImVec2 pos = pos_min;
2149 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
2150
2151 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
2152 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
2153 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
2154 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
2155 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
2156
2157 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
2158 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
2159 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
2160
2161 // Render
2162 if (need_clipping)
2163 {
2164 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
2165 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
2166 }
2167 else
2168 {
2169 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
2170 }
2171 if (g.LogEnabled)
2172 LogRenderedText(&pos, text, text_display_end);
2173 }
2174
2175 // Render a rectangle shaped with optional rounding and borders
RenderFrame(ImVec2 p_min,ImVec2 p_max,ImU32 fill_col,bool border,float rounding)2176 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)
2177 {
2178 ImGuiContext& g = *GImGui;
2179 ImGuiWindow* window = g.CurrentWindow;
2180 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
2181 const float border_size = g.Style.FrameBorderSize;
2182 if (border && border_size > 0.0f)
2183 {
2184 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2185 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2186 }
2187 }
2188
RenderFrameBorder(ImVec2 p_min,ImVec2 p_max,float rounding)2189 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
2190 {
2191 ImGuiContext& g = *GImGui;
2192 ImGuiWindow* window = g.CurrentWindow;
2193 const float border_size = g.Style.FrameBorderSize;
2194 if (border_size > 0.0f)
2195 {
2196 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size);
2197 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size);
2198 }
2199 }
2200
2201 // 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)2202 void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale)
2203 {
2204 ImGuiContext& g = *GImGui;
2205
2206 const float h = g.FontSize * 1.00f;
2207 float r = h * 0.40f * scale;
2208 ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale);
2209
2210 ImVec2 a, b, c;
2211 switch (dir)
2212 {
2213 case ImGuiDir_Up:
2214 case ImGuiDir_Down:
2215 if (dir == ImGuiDir_Up) r = -r;
2216 a = ImVec2(+0.000f,+0.750f) * r;
2217 b = ImVec2(-0.866f,-0.750f) * r;
2218 c = ImVec2(+0.866f,-0.750f) * r;
2219 break;
2220 case ImGuiDir_Left:
2221 case ImGuiDir_Right:
2222 if (dir == ImGuiDir_Left) r = -r;
2223 a = ImVec2(+0.750f,+0.000f) * r;
2224 b = ImVec2(-0.750f,+0.866f) * r;
2225 c = ImVec2(-0.750f,-0.866f) * r;
2226 break;
2227 case ImGuiDir_None:
2228 case ImGuiDir_COUNT:
2229 IM_ASSERT(0);
2230 break;
2231 }
2232
2233 g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text));
2234 }
2235
RenderBullet(ImVec2 pos)2236 void ImGui::RenderBullet(ImVec2 pos)
2237 {
2238 ImGuiContext& g = *GImGui;
2239 ImGuiWindow* window = g.CurrentWindow;
2240 window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8);
2241 }
2242
RenderCheckMark(ImVec2 pos,ImU32 col,float sz)2243 void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz)
2244 {
2245 ImGuiContext& g = *GImGui;
2246 ImGuiWindow* window = g.CurrentWindow;
2247
2248 float thickness = ImMax(sz / 5.0f, 1.0f);
2249 sz -= thickness*0.5f;
2250 pos += ImVec2(thickness*0.25f, thickness*0.25f);
2251
2252 float third = sz / 3.0f;
2253 float bx = pos.x + third;
2254 float by = pos.y + sz - third*0.5f;
2255 window->DrawList->PathLineTo(ImVec2(bx - third, by - third));
2256 window->DrawList->PathLineTo(ImVec2(bx, by));
2257 window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2));
2258 window->DrawList->PathStroke(col, false, thickness);
2259 }
2260
RenderNavHighlight(const ImRect & bb,ImGuiID id,ImGuiNavHighlightFlags flags)2261 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)
2262 {
2263 ImGuiContext& g = *GImGui;
2264 if (id != g.NavId)
2265 return;
2266 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))
2267 return;
2268 ImGuiWindow* window = ImGui::GetCurrentWindow();
2269 if (window->DC.NavHideHighlightOneFrame)
2270 return;
2271
2272 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
2273 ImRect display_rect = bb;
2274 display_rect.ClipWith(window->ClipRect);
2275 if (flags & ImGuiNavHighlightFlags_TypeDefault)
2276 {
2277 const float THICKNESS = 2.0f;
2278 const float DISTANCE = 3.0f + THICKNESS * 0.5f;
2279 display_rect.Expand(ImVec2(DISTANCE,DISTANCE));
2280 bool fully_visible = window->ClipRect.Contains(display_rect);
2281 if (!fully_visible)
2282 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
2283 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);
2284 if (!fully_visible)
2285 window->DrawList->PopClipRect();
2286 }
2287 if (flags & ImGuiNavHighlightFlags_TypeThin)
2288 {
2289 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f);
2290 }
2291 }
2292
2293 //-----------------------------------------------------------------------------
2294 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
2295 //-----------------------------------------------------------------------------
2296
2297 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
ImGuiWindow(ImGuiContext * context,const char * name)2298 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
2299 : DrawListInst(&context->DrawListSharedData)
2300 {
2301 Name = ImStrdup(name);
2302 ID = ImHash(name, 0);
2303 IDStack.push_back(ID);
2304 Flags = 0;
2305 Pos = ImVec2(0.0f, 0.0f);
2306 Size = SizeFull = ImVec2(0.0f, 0.0f);
2307 SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f);
2308 WindowPadding = ImVec2(0.0f, 0.0f);
2309 WindowRounding = 0.0f;
2310 WindowBorderSize = 0.0f;
2311 MoveId = GetID("#MOVE");
2312 ChildId = 0;
2313 Scroll = ImVec2(0.0f, 0.0f);
2314 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
2315 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
2316 ScrollbarSizes = ImVec2(0.0f, 0.0f);
2317 ScrollbarX = ScrollbarY = false;
2318 Active = WasActive = false;
2319 WriteAccessed = false;
2320 Collapsed = false;
2321 WantCollapseToggle = false;
2322 SkipItems = false;
2323 Appearing = false;
2324 Hidden = false;
2325 HasCloseButton = false;
2326 BeginCount = 0;
2327 BeginOrderWithinParent = -1;
2328 BeginOrderWithinContext = -1;
2329 PopupId = 0;
2330 AutoFitFramesX = AutoFitFramesY = -1;
2331 AutoFitOnlyGrows = false;
2332 AutoFitChildAxises = 0x00;
2333 AutoPosLastDirection = ImGuiDir_None;
2334 HiddenFramesRegular = HiddenFramesForResize = 0;
2335 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
2336 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
2337
2338 LastFrameActive = -1;
2339 ItemWidthDefault = 0.0f;
2340 FontWindowScale = 1.0f;
2341 SettingsIdx = -1;
2342
2343 DrawList = &DrawListInst;
2344 DrawList->_OwnerName = Name;
2345 ParentWindow = NULL;
2346 RootWindow = NULL;
2347 RootWindowForTitleBarHighlight = NULL;
2348 RootWindowForNav = NULL;
2349
2350 NavLastIds[0] = NavLastIds[1] = 0;
2351 NavRectRel[0] = NavRectRel[1] = ImRect();
2352 NavLastChildNavWindow = NULL;
2353
2354 FocusIdxAllCounter = FocusIdxTabCounter = -1;
2355 FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
2356 FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
2357 }
2358
~ImGuiWindow()2359 ImGuiWindow::~ImGuiWindow()
2360 {
2361 IM_ASSERT(DrawList == &DrawListInst);
2362 IM_DELETE(Name);
2363 for (int i = 0; i != ColumnsStorage.Size; i++)
2364 ColumnsStorage[i].~ImGuiColumnsSet();
2365 }
2366
GetID(const char * str,const char * str_end)2367 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
2368 {
2369 ImGuiID seed = IDStack.back();
2370 ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2371 ImGui::KeepAliveID(id);
2372 return id;
2373 }
2374
GetID(const void * ptr)2375 ImGuiID ImGuiWindow::GetID(const void* ptr)
2376 {
2377 ImGuiID seed = IDStack.back();
2378 ImGuiID id = ImHash(&ptr, sizeof(void*), seed);
2379 ImGui::KeepAliveID(id);
2380 return id;
2381 }
2382
GetIDNoKeepAlive(const char * str,const char * str_end)2383 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
2384 {
2385 ImGuiID seed = IDStack.back();
2386 return ImHash(str, str_end ? (int)(str_end - str) : 0, seed);
2387 }
2388
GetIDNoKeepAlive(const void * ptr)2389 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
2390 {
2391 ImGuiID seed = IDStack.back();
2392 return ImHash(&ptr, sizeof(void*), seed);
2393 }
2394
2395 // This is only used in rare/specific situations to manufacture an ID out of nowhere.
GetIDFromRectangle(const ImRect & r_abs)2396 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
2397 {
2398 ImGuiID seed = IDStack.back();
2399 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) };
2400 ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed);
2401 ImGui::KeepAliveID(id);
2402 return id;
2403 }
2404
SetCurrentWindow(ImGuiWindow * window)2405 static void SetCurrentWindow(ImGuiWindow* window)
2406 {
2407 ImGuiContext& g = *GImGui;
2408 g.CurrentWindow = window;
2409 if (window)
2410 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
2411 }
2412
SetNavID(ImGuiID id,int nav_layer)2413 void ImGui::SetNavID(ImGuiID id, int nav_layer)
2414 {
2415 ImGuiContext& g = *GImGui;
2416 IM_ASSERT(g.NavWindow);
2417 IM_ASSERT(nav_layer == 0 || nav_layer == 1);
2418 g.NavId = id;
2419 g.NavWindow->NavLastIds[nav_layer] = id;
2420 }
2421
SetNavIDWithRectRel(ImGuiID id,int nav_layer,const ImRect & rect_rel)2422 void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel)
2423 {
2424 ImGuiContext& g = *GImGui;
2425 SetNavID(id, nav_layer);
2426 g.NavWindow->NavRectRel[nav_layer] = rect_rel;
2427 g.NavMousePosDirty = true;
2428 g.NavDisableHighlight = false;
2429 g.NavDisableMouseHover = true;
2430 }
2431
SetActiveID(ImGuiID id,ImGuiWindow * window)2432 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
2433 {
2434 ImGuiContext& g = *GImGui;
2435 g.ActiveIdIsJustActivated = (g.ActiveId != id);
2436 if (g.ActiveIdIsJustActivated)
2437 {
2438 g.ActiveIdTimer = 0.0f;
2439 g.ActiveIdHasBeenEdited = false;
2440 if (id != 0)
2441 {
2442 g.LastActiveId = id;
2443 g.LastActiveIdTimer = 0.0f;
2444 }
2445 }
2446 g.ActiveId = id;
2447 g.ActiveIdAllowNavDirFlags = 0;
2448 g.ActiveIdAllowOverlap = false;
2449 g.ActiveIdWindow = window;
2450 if (id)
2451 {
2452 g.ActiveIdIsAlive = id;
2453 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse;
2454 }
2455 }
2456
SetFocusID(ImGuiID id,ImGuiWindow * window)2457 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
2458 {
2459 ImGuiContext& g = *GImGui;
2460 IM_ASSERT(id != 0);
2461
2462 // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it.
2463 const int nav_layer = window->DC.NavLayerCurrent;
2464 if (g.NavWindow != window)
2465 g.NavInitRequest = false;
2466 g.NavId = id;
2467 g.NavWindow = window;
2468 g.NavLayer = nav_layer;
2469 window->NavLastIds[nav_layer] = id;
2470 if (window->DC.LastItemId == id)
2471 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos);
2472
2473 if (g.ActiveIdSource == ImGuiInputSource_Nav)
2474 g.NavDisableMouseHover = true;
2475 else
2476 g.NavDisableHighlight = true;
2477 }
2478
ClearActiveID()2479 void ImGui::ClearActiveID()
2480 {
2481 SetActiveID(0, NULL);
2482 }
2483
SetHoveredID(ImGuiID id)2484 void ImGui::SetHoveredID(ImGuiID id)
2485 {
2486 ImGuiContext& g = *GImGui;
2487 g.HoveredId = id;
2488 g.HoveredIdAllowOverlap = false;
2489 if (id != 0 && g.HoveredIdPreviousFrame != id)
2490 g.HoveredIdTimer = 0.0f;
2491 }
2492
GetHoveredID()2493 ImGuiID ImGui::GetHoveredID()
2494 {
2495 ImGuiContext& g = *GImGui;
2496 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
2497 }
2498
KeepAliveID(ImGuiID id)2499 void ImGui::KeepAliveID(ImGuiID id)
2500 {
2501 ImGuiContext& g = *GImGui;
2502 if (g.ActiveId == id)
2503 g.ActiveIdIsAlive = id;
2504 if (g.ActiveIdPreviousFrame == id)
2505 g.ActiveIdPreviousFrameIsAlive = true;
2506 }
2507
MarkItemEdited(ImGuiID id)2508 void ImGui::MarkItemEdited(ImGuiID id)
2509 {
2510 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().
2511 // 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.
2512 (void)id; // Avoid unused variable warnings when asserts are compiled out.
2513 ImGuiContext& g = *GImGui;
2514 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive);
2515 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
2516 g.ActiveIdHasBeenEdited = true;
2517 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited;
2518 }
2519
IsWindowContentHoverable(ImGuiWindow * window,ImGuiHoveredFlags flags)2520 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
2521 {
2522 // An active popup disable hovering on other windows (apart from its own children)
2523 // FIXME-OPT: This could be cached/stored within the window.
2524 ImGuiContext& g = *GImGui;
2525 if (g.NavWindow)
2526 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
2527 if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
2528 {
2529 // For the purpose of those flags we differentiate "standard popup" from "modal popup"
2530 // NB: The order of those two tests is important because Modal windows are also Popups.
2531 if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
2532 return false;
2533 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
2534 return false;
2535 }
2536
2537 return true;
2538 }
2539
2540 // Advance cursor given item size for layout.
ItemSize(const ImVec2 & size,float text_offset_y)2541 void ImGui::ItemSize(const ImVec2& size, float text_offset_y)
2542 {
2543 ImGuiContext& g = *GImGui;
2544 ImGuiWindow* window = g.CurrentWindow;
2545 if (window->SkipItems)
2546 return;
2547
2548 // Always align ourselves on pixel boundaries
2549 const float line_height = ImMax(window->DC.CurrentLineSize.y, size.y);
2550 const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y);
2551 //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]
2552 window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y);
2553 window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y));
2554 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
2555 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
2556 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
2557
2558 window->DC.PrevLineSize.y = line_height;
2559 window->DC.PrevLineTextBaseOffset = text_base_offset;
2560 window->DC.CurrentLineSize.y = window->DC.CurrentLineTextBaseOffset = 0.0f;
2561
2562 // Horizontal layout mode
2563 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
2564 SameLine();
2565 }
2566
ItemSize(const ImRect & bb,float text_offset_y)2567 void ImGui::ItemSize(const ImRect& bb, float text_offset_y)
2568 {
2569 ItemSize(bb.GetSize(), text_offset_y);
2570 }
2571
2572 // Declare item bounding box for clipping and interaction.
2573 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
2574 // 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)2575 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg)
2576 {
2577 ImGuiContext& g = *GImGui;
2578 ImGuiWindow* window = g.CurrentWindow;
2579
2580 if (id != 0)
2581 {
2582 // Navigation processing runs prior to clipping early-out
2583 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
2584 // (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.
2585 // 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.
2586 // 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)
2587 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask;
2588 if (g.NavId == id || g.NavAnyRequest)
2589 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
2590 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))
2591 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id);
2592 }
2593
2594 window->DC.LastItemId = id;
2595 window->DC.LastItemRect = bb;
2596 window->DC.LastItemStatusFlags = 0;
2597
2598 // Clipping test
2599 const bool is_clipped = IsClippedEx(bb, id, false);
2600 if (is_clipped)
2601 return false;
2602 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
2603
2604 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
2605 if (IsMouseHoveringRect(bb.Min, bb.Max))
2606 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect;
2607 return true;
2608 }
2609
2610 // This is roughly matching the behavior of internal-facing ItemHoverable()
2611 // - 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()
2612 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
IsItemHovered(ImGuiHoveredFlags flags)2613 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
2614 {
2615 ImGuiContext& g = *GImGui;
2616 ImGuiWindow* window = g.CurrentWindow;
2617 if (g.NavDisableMouseHover && !g.NavDisableHighlight)
2618 return IsItemFocused();
2619
2620 // Test for bounding box overlap, as updated as ItemAdd()
2621 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
2622 return false;
2623 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function
2624
2625 // Test if we are hovering the right window (our window could be behind another window)
2626 // [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.
2627 // 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.
2628 //if (g.HoveredWindow != window)
2629 // return false;
2630 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
2631 return false;
2632
2633 // Test if another item is active (e.g. being dragged)
2634 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
2635 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
2636 return false;
2637
2638 // Test if interactions on this window are blocked by an active popup or modal
2639 if (!IsWindowContentHoverable(window, flags))
2640 return false;
2641
2642 // Test if the item is disabled
2643 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
2644 return false;
2645
2646 // 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.
2647 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
2648 return false;
2649 return true;
2650 }
2651
2652 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
ItemHoverable(const ImRect & bb,ImGuiID id)2653 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
2654 {
2655 ImGuiContext& g = *GImGui;
2656 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
2657 return false;
2658
2659 ImGuiWindow* window = g.CurrentWindow;
2660 if (g.HoveredWindow != window)
2661 return false;
2662 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
2663 return false;
2664 if (!IsMouseHoveringRect(bb.Min, bb.Max))
2665 return false;
2666 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
2667 return false;
2668 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled)
2669 return false;
2670
2671 SetHoveredID(id);
2672 return true;
2673 }
2674
IsClippedEx(const ImRect & bb,ImGuiID id,bool clip_even_when_logged)2675 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged)
2676 {
2677 ImGuiContext& g = *GImGui;
2678 ImGuiWindow* window = g.CurrentWindow;
2679 if (!bb.Overlaps(window->ClipRect))
2680 if (id == 0 || id != g.ActiveId)
2681 if (clip_even_when_logged || !g.LogEnabled)
2682 return true;
2683 return false;
2684 }
2685
FocusableItemRegister(ImGuiWindow * window,ImGuiID id,bool tab_stop)2686 bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop)
2687 {
2688 ImGuiContext& g = *GImGui;
2689
2690 const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus;
2691 window->FocusIdxAllCounter++;
2692 if (allow_keyboard_focus)
2693 window->FocusIdxTabCounter++;
2694
2695 // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item.
2696 // Note that we can always TAB out of a widget that doesn't allow tabbing in.
2697 if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab))
2698 window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items.
2699
2700 if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent)
2701 return true;
2702 if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent)
2703 {
2704 g.NavJustTabbedId = id;
2705 return true;
2706 }
2707
2708 return false;
2709 }
2710
FocusableItemUnregister(ImGuiWindow * window)2711 void ImGui::FocusableItemUnregister(ImGuiWindow* window)
2712 {
2713 window->FocusIdxAllCounter--;
2714 window->FocusIdxTabCounter--;
2715 }
2716
CalcItemSize(ImVec2 size,float default_x,float default_y)2717 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y)
2718 {
2719 ImGuiContext& g = *GImGui;
2720 ImVec2 content_max;
2721 if (size.x < 0.0f || size.y < 0.0f)
2722 content_max = g.CurrentWindow->Pos + GetContentRegionMax();
2723 if (size.x <= 0.0f)
2724 size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x;
2725 if (size.y <= 0.0f)
2726 size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y;
2727 return size;
2728 }
2729
CalcWrapWidthForPos(const ImVec2 & pos,float wrap_pos_x)2730 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
2731 {
2732 if (wrap_pos_x < 0.0f)
2733 return 0.0f;
2734
2735 ImGuiWindow* window = GetCurrentWindowRead();
2736 if (wrap_pos_x == 0.0f)
2737 wrap_pos_x = GetContentRegionMax().x + window->Pos.x;
2738 else if (wrap_pos_x > 0.0f)
2739 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
2740
2741 return ImMax(wrap_pos_x - pos.x, 1.0f);
2742 }
2743
MemAlloc(size_t size)2744 void* ImGui::MemAlloc(size_t size)
2745 {
2746 if (ImGuiContext* ctx = GImGui)
2747 ctx->IO.MetricsActiveAllocations++;
2748 return GImAllocatorAllocFunc(size, GImAllocatorUserData);
2749 }
2750
MemFree(void * ptr)2751 void ImGui::MemFree(void* ptr)
2752 {
2753 if (ptr)
2754 if (ImGuiContext* ctx = GImGui)
2755 ctx->IO.MetricsActiveAllocations--;
2756 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
2757 }
2758
GetClipboardText()2759 const char* ImGui::GetClipboardText()
2760 {
2761 return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : "";
2762 }
2763
SetClipboardText(const char * text)2764 void ImGui::SetClipboardText(const char* text)
2765 {
2766 if (GImGui->IO.SetClipboardTextFn)
2767 GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text);
2768 }
2769
GetVersion()2770 const char* ImGui::GetVersion()
2771 {
2772 return IMGUI_VERSION;
2773 }
2774
2775 // Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself
2776 // 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()2777 ImGuiContext* ImGui::GetCurrentContext()
2778 {
2779 return GImGui;
2780 }
2781
SetCurrentContext(ImGuiContext * ctx)2782 void ImGui::SetCurrentContext(ImGuiContext* ctx)
2783 {
2784 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
2785 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
2786 #else
2787 GImGui = ctx;
2788 #endif
2789 }
2790
2791 // Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
2792 // 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)2793 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)
2794 {
2795 bool error = false;
2796 if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatched version string!"); }
2797 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
2798 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
2799 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
2800 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
2801 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
2802 return !error;
2803 }
2804
SetAllocatorFunctions(void * (* alloc_func)(size_t sz,void * user_data),void (* free_func)(void * ptr,void * user_data),void * user_data)2805 void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data)
2806 {
2807 GImAllocatorAllocFunc = alloc_func;
2808 GImAllocatorFreeFunc = free_func;
2809 GImAllocatorUserData = user_data;
2810 }
2811
CreateContext(ImFontAtlas * shared_font_atlas)2812 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
2813 {
2814 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
2815 if (GImGui == NULL)
2816 SetCurrentContext(ctx);
2817 Initialize(ctx);
2818 return ctx;
2819 }
2820
DestroyContext(ImGuiContext * ctx)2821 void ImGui::DestroyContext(ImGuiContext* ctx)
2822 {
2823 if (ctx == NULL)
2824 ctx = GImGui;
2825 Shutdown(ctx);
2826 if (GImGui == ctx)
2827 SetCurrentContext(NULL);
2828 IM_DELETE(ctx);
2829 }
2830
GetIO()2831 ImGuiIO& ImGui::GetIO()
2832 {
2833 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2834 return GImGui->IO;
2835 }
2836
GetStyle()2837 ImGuiStyle& ImGui::GetStyle()
2838 {
2839 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
2840 return GImGui->Style;
2841 }
2842
2843 // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame()
GetDrawData()2844 ImDrawData* ImGui::GetDrawData()
2845 {
2846 ImGuiContext& g = *GImGui;
2847 return g.DrawData.Valid ? &g.DrawData : NULL;
2848 }
2849
GetTime()2850 double ImGui::GetTime()
2851 {
2852 return GImGui->Time;
2853 }
2854
GetFrameCount()2855 int ImGui::GetFrameCount()
2856 {
2857 return GImGui->FrameCount;
2858 }
2859
GetOverlayDrawList()2860 ImDrawList* ImGui::GetOverlayDrawList()
2861 {
2862 return &GImGui->OverlayDrawList;
2863 }
2864
GetDrawListSharedData()2865 ImDrawListSharedData* ImGui::GetDrawListSharedData()
2866 {
2867 return &GImGui->DrawListSharedData;
2868 }
2869
StartMouseMovingWindow(ImGuiWindow * window)2870 void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
2871 {
2872 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
2873 ImGuiContext& g = *GImGui;
2874 FocusWindow(window);
2875 SetActiveID(window->MoveId, window);
2876 g.NavDisableHighlight = true;
2877 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos;
2878 if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
2879 g.MovingWindow = window;
2880 }
2881
2882 // Handle mouse moving window
2883 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
UpdateMouseMovingWindow()2884 void ImGui::UpdateMouseMovingWindow()
2885 {
2886 ImGuiContext& g = *GImGui;
2887 if (g.MovingWindow != NULL)
2888 {
2889 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
2890 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
2891 KeepAliveID(g.ActiveId);
2892 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
2893 ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
2894 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
2895 {
2896 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
2897 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y)
2898 {
2899 MarkIniSettingsDirty(moving_window);
2900 SetWindowPos(moving_window, pos, ImGuiCond_Always);
2901 }
2902 FocusWindow(g.MovingWindow);
2903 }
2904 else
2905 {
2906 ClearActiveID();
2907 g.MovingWindow = NULL;
2908 }
2909 }
2910 else
2911 {
2912 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
2913 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
2914 {
2915 KeepAliveID(g.ActiveId);
2916 if (!g.IO.MouseDown[0])
2917 ClearActiveID();
2918 }
2919 }
2920 }
2921
IsWindowActiveAndVisible(ImGuiWindow * window)2922 static bool IsWindowActiveAndVisible(ImGuiWindow* window)
2923 {
2924 return (window->Active) && (!window->Hidden);
2925 }
2926
UpdateMouseInputs()2927 static void ImGui::UpdateMouseInputs()
2928 {
2929 ImGuiContext& g = *GImGui;
2930
2931 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
2932 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev))
2933 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev;
2934 else
2935 g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
2936 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f)
2937 g.NavDisableMouseHover = false;
2938
2939 g.IO.MousePosPrev = g.IO.MousePos;
2940 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
2941 {
2942 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f;
2943 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f;
2944 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i];
2945 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;
2946 g.IO.MouseDoubleClicked[i] = false;
2947 if (g.IO.MouseClicked[i])
2948 {
2949 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime)
2950 {
2951 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
2952 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist)
2953 g.IO.MouseDoubleClicked[i] = true;
2954 g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click
2955 }
2956 else
2957 {
2958 g.IO.MouseClickedTime[i] = g.Time;
2959 }
2960 g.IO.MouseClickedPos[i] = g.IO.MousePos;
2961 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f);
2962 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f;
2963 }
2964 else if (g.IO.MouseDown[i])
2965 {
2966 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
2967 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
2968 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos));
2969 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);
2970 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);
2971 }
2972 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation
2973 g.NavDisableMouseHover = false;
2974 }
2975 }
2976
UpdateMouseWheel()2977 void ImGui::UpdateMouseWheel()
2978 {
2979 ImGuiContext& g = *GImGui;
2980 if (!g.HoveredWindow || g.HoveredWindow->Collapsed)
2981 return;
2982 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f)
2983 return;
2984
2985 // 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).
2986 ImGuiWindow* window = g.HoveredWindow;
2987 ImGuiWindow* scroll_window = window;
2988 while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow)
2989 scroll_window = scroll_window->ParentWindow;
2990 const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs);
2991
2992 if (g.IO.MouseWheel != 0.0f)
2993 {
2994 if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
2995 {
2996 // Zoom / Scale window
2997 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
2998 const float scale = new_font_scale / window->FontWindowScale;
2999 window->FontWindowScale = new_font_scale;
3000
3001 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
3002 window->Pos += offset;
3003 window->Size *= scale;
3004 window->SizeFull *= scale;
3005 }
3006 else if (!g.IO.KeyCtrl && scroll_allowed)
3007 {
3008 // Mouse wheel vertical scrolling
3009 float scroll_amount = 5 * scroll_window->CalcFontSize();
3010 scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f);
3011 SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount);
3012 }
3013 }
3014 if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl)
3015 {
3016 // Mouse wheel horizontal scrolling (for hardware that supports it)
3017 float scroll_amount = scroll_window->CalcFontSize();
3018 SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount);
3019 }
3020 }
3021
3022 // 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()3023 void ImGui::UpdateHoveredWindowAndCaptureFlags()
3024 {
3025 ImGuiContext& g = *GImGui;
3026
3027 // Find the window hovered by mouse:
3028 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
3029 // - 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.
3030 // - 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.
3031 FindHoveredWindow();
3032
3033 // Modal windows prevents cursor from hovering behind them.
3034 ImGuiWindow* modal_window = GetFrontMostPopupModal();
3035 if (modal_window)
3036 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
3037 g.HoveredRootWindow = g.HoveredWindow = NULL;
3038
3039 // Disabled mouse?
3040 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
3041 g.HoveredWindow = g.HoveredRootWindow = NULL;
3042
3043 // 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.
3044 int mouse_earliest_button_down = -1;
3045 bool mouse_any_down = false;
3046 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
3047 {
3048 if (g.IO.MouseClicked[i])
3049 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty());
3050 mouse_any_down |= g.IO.MouseDown[i];
3051 if (g.IO.MouseDown[i])
3052 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down])
3053 mouse_earliest_button_down = i;
3054 }
3055 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];
3056
3057 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
3058 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
3059 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
3060 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
3061 g.HoveredWindow = g.HoveredRootWindow = NULL;
3062
3063 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
3064 if (g.WantCaptureMouseNextFrame != -1)
3065 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0);
3066 else
3067 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty());
3068
3069 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app)
3070 if (g.WantCaptureKeyboardNextFrame != -1)
3071 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
3072 else
3073 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);
3074 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))
3075 g.IO.WantCaptureKeyboard = true;
3076
3077 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
3078 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
3079 }
3080
NewFrame()3081 void ImGui::NewFrame()
3082 {
3083 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?");
3084 ImGuiContext& g = *GImGui;
3085
3086 // Check user data
3087 // (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)
3088 IM_ASSERT(g.Initialized);
3089 IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)");
3090 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value");
3091 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3092 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
3093 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting");
3094 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)");
3095 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
3096 for (int n = 0; n < ImGuiKey_COUNT; n++)
3097 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)");
3098
3099 // 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)
3100 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
3101 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
3102
3103 // Perform simple check: the beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
3104 if (g.IO.ConfigResizeWindowsFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
3105 g.IO.ConfigResizeWindowsFromEdges = false;
3106
3107 // Load settings on first frame (if not explicitly loaded manually before)
3108 if (!g.SettingsLoaded)
3109 {
3110 IM_ASSERT(g.SettingsWindows.empty());
3111 if (g.IO.IniFilename)
3112 LoadIniSettingsFromDisk(g.IO.IniFilename);
3113 g.SettingsLoaded = true;
3114 }
3115
3116 // Save settings (with a delay after the last modification, so we don't spam disk too much)
3117 if (g.SettingsDirtyTimer > 0.0f)
3118 {
3119 g.SettingsDirtyTimer -= g.IO.DeltaTime;
3120 if (g.SettingsDirtyTimer <= 0.0f)
3121 {
3122 if (g.IO.IniFilename != NULL)
3123 SaveIniSettingsToDisk(g.IO.IniFilename);
3124 else
3125 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
3126 g.SettingsDirtyTimer = 0.0f;
3127 }
3128 }
3129
3130 g.Time += g.IO.DeltaTime;
3131 g.FrameScopeActive = true;
3132 g.FrameCount += 1;
3133 g.TooltipOverrideCount = 0;
3134 g.WindowsActiveCount = 0;
3135
3136 // Setup current font and draw list
3137 g.IO.Fonts->Locked = true;
3138 SetCurrentFont(GetDefaultFont());
3139 IM_ASSERT(g.Font->IsLoaded());
3140 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
3141 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
3142
3143 g.OverlayDrawList.Clear();
3144 g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID);
3145 g.OverlayDrawList.PushClipRectFullScreen();
3146 g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
3147
3148 // Mark rendering data as invalid to prevent user who may have a handle on it to use it
3149 g.DrawData.Clear();
3150
3151 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
3152 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
3153 KeepAliveID(g.DragDropPayload.SourceId);
3154
3155 // Clear reference to active widget if the widget isn't alive anymore
3156 if (!g.HoveredIdPreviousFrame)
3157 g.HoveredIdTimer = 0.0f;
3158 if (g.HoveredId)
3159 g.HoveredIdTimer += g.IO.DeltaTime;
3160 g.HoveredIdPreviousFrame = g.HoveredId;
3161 g.HoveredId = 0;
3162 g.HoveredIdAllowOverlap = false;
3163 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0)
3164 ClearActiveID();
3165 if (g.ActiveId)
3166 g.ActiveIdTimer += g.IO.DeltaTime;
3167 g.LastActiveIdTimer += g.IO.DeltaTime;
3168 g.ActiveIdPreviousFrame = g.ActiveId;
3169 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;
3170 g.ActiveIdPreviousFrameHasBeenEdited = g.ActiveIdHasBeenEdited;
3171 g.ActiveIdIsAlive = 0;
3172 g.ActiveIdPreviousFrameIsAlive = false;
3173 g.ActiveIdIsJustActivated = false;
3174 if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId)
3175 g.ScalarAsInputTextId = 0;
3176
3177 // Drag and drop
3178 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
3179 g.DragDropAcceptIdCurr = 0;
3180 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
3181 g.DragDropWithinSourceOrTarget = false;
3182
3183 // Update keyboard input state
3184 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
3185 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
3186 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;
3187
3188 // Update gamepad/keyboard directional navigation
3189 NavUpdate();
3190
3191 // Update mouse input state
3192 UpdateMouseInputs();
3193
3194 // Calculate frame-rate for the user, as a purely luxurious feature
3195 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
3196 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
3197 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
3198 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
3199
3200 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
3201 UpdateMouseMovingWindow();
3202 UpdateHoveredWindowAndCaptureFlags();
3203
3204 // Background darkening/whitening
3205 if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
3206 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
3207 else
3208 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
3209
3210 g.MouseCursor = ImGuiMouseCursor_Arrow;
3211 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
3212 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default
3213
3214 // Mouse wheel scrolling, scale
3215 UpdateMouseWheel();
3216
3217 // Pressing TAB activate widget focus
3218 if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false))
3219 {
3220 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX)
3221 g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1);
3222 else
3223 g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0;
3224 }
3225 g.NavIdTabCounter = INT_MAX;
3226
3227 // Mark all windows as not visible
3228 for (int i = 0; i != g.Windows.Size; i++)
3229 {
3230 ImGuiWindow* window = g.Windows[i];
3231 window->WasActive = window->Active;
3232 window->Active = false;
3233 window->WriteAccessed = false;
3234 }
3235
3236 // Closing the focused window restore focus to the first active root window in descending z-order
3237 if (g.NavWindow && !g.NavWindow->WasActive)
3238 FocusFrontMostActiveWindowIgnoringOne(NULL);
3239
3240 // No window should be open at the beginning of the frame.
3241 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
3242 g.CurrentWindowStack.resize(0);
3243 g.CurrentPopupStack.resize(0);
3244 ClosePopupsOverWindow(g.NavWindow);
3245
3246 // Create implicit window - we will only render it if the user has added something to it.
3247 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
3248 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
3249 Begin("Debug##Default");
3250 }
3251
Initialize(ImGuiContext * context)3252 void ImGui::Initialize(ImGuiContext* context)
3253 {
3254 ImGuiContext& g = *context;
3255 IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
3256
3257 // Add .ini handle for ImGuiWindow type
3258 ImGuiSettingsHandler ini_handler;
3259 ini_handler.TypeName = "Window";
3260 ini_handler.TypeHash = ImHash("Window", 0, 0);
3261 ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
3262 ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
3263 ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
3264 g.SettingsHandlers.push_front(ini_handler);
3265
3266 g.Initialized = true;
3267 }
3268
3269 // This function is merely here to free heap allocations.
Shutdown(ImGuiContext * context)3270 void ImGui::Shutdown(ImGuiContext* context)
3271 {
3272 // 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)
3273 ImGuiContext& g = *context;
3274 if (g.IO.Fonts && g.FontAtlasOwnedByContext)
3275 IM_DELETE(g.IO.Fonts);
3276 g.IO.Fonts = NULL;
3277
3278 // Cleanup of other data are conditional on actually having initialized ImGui.
3279 if (!g.Initialized)
3280 return;
3281
3282 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
3283 if (g.SettingsLoaded && g.IO.IniFilename != NULL)
3284 SaveIniSettingsToDisk(g.IO.IniFilename);
3285
3286 // Clear everything else
3287 for (int i = 0; i < g.Windows.Size; i++)
3288 IM_DELETE(g.Windows[i]);
3289 g.Windows.clear();
3290 g.WindowsSortBuffer.clear();
3291 g.CurrentWindow = NULL;
3292 g.CurrentWindowStack.clear();
3293 g.WindowsById.Clear();
3294 g.NavWindow = NULL;
3295 g.HoveredWindow = NULL;
3296 g.HoveredRootWindow = NULL;
3297 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
3298 g.MovingWindow = NULL;
3299 g.ColorModifiers.clear();
3300 g.StyleModifiers.clear();
3301 g.FontStack.clear();
3302 g.OpenPopupStack.clear();
3303 g.CurrentPopupStack.clear();
3304 g.DrawDataBuilder.ClearFreeMemory();
3305 g.OverlayDrawList.ClearFreeMemory();
3306 g.PrivateClipboard.clear();
3307 g.InputTextState.TextW.clear();
3308 g.InputTextState.InitialText.clear();
3309 g.InputTextState.TempBuffer.clear();
3310
3311 for (int i = 0; i < g.SettingsWindows.Size; i++)
3312 IM_DELETE(g.SettingsWindows[i].Name);
3313 g.SettingsWindows.clear();
3314 g.SettingsHandlers.clear();
3315
3316 if (g.LogFile && g.LogFile != stdout)
3317 {
3318 fclose(g.LogFile);
3319 g.LogFile = NULL;
3320 }
3321 g.LogClipboard.clear();
3322
3323 g.Initialized = false;
3324 }
3325
3326 // FIXME: Add a more explicit sort order in the window structure.
ChildWindowComparer(const void * lhs,const void * rhs)3327 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
3328 {
3329 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
3330 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
3331 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
3332 return d;
3333 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
3334 return d;
3335 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
3336 }
3337
AddWindowToSortedBuffer(ImVector<ImGuiWindow * > * out_sorted_windows,ImGuiWindow * window)3338 static void AddWindowToSortedBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
3339 {
3340 out_sorted_windows->push_back(window);
3341 if (window->Active)
3342 {
3343 int count = window->DC.ChildWindows.Size;
3344 if (count > 1)
3345 ImQsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
3346 for (int i = 0; i < count; i++)
3347 {
3348 ImGuiWindow* child = window->DC.ChildWindows[i];
3349 if (child->Active)
3350 AddWindowToSortedBuffer(out_sorted_windows, child);
3351 }
3352 }
3353 }
3354
AddDrawListToDrawData(ImVector<ImDrawList * > * out_list,ImDrawList * draw_list)3355 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
3356 {
3357 if (draw_list->CmdBuffer.empty())
3358 return;
3359
3360 // Remove trailing command if unused
3361 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back();
3362 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL)
3363 {
3364 draw_list->CmdBuffer.pop_back();
3365 if (draw_list->CmdBuffer.empty())
3366 return;
3367 }
3368
3369 // 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.
3370 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
3371 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
3372 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
3373
3374 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
3375 // If this assert triggers because you are drawing lots of stuff manually:
3376 // 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.
3377 // 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.
3378 // 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:
3379 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
3380 // 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.
3381 // 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.
3382 if (sizeof(ImDrawIdx) == 2)
3383 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
3384
3385 out_list->push_back(draw_list);
3386 }
3387
AddWindowToDrawData(ImVector<ImDrawList * > * out_render_list,ImGuiWindow * window)3388 static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window)
3389 {
3390 ImGuiContext& g = *GImGui;
3391 g.IO.MetricsRenderWindows++;
3392 AddDrawListToDrawData(out_render_list, window->DrawList);
3393 for (int i = 0; i < window->DC.ChildWindows.Size; i++)
3394 {
3395 ImGuiWindow* child = window->DC.ChildWindows[i];
3396 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active
3397 AddWindowToDrawData(out_render_list, child);
3398 }
3399 }
3400
AddWindowToDrawDataSelectLayer(ImGuiWindow * window)3401 static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window)
3402 {
3403 ImGuiContext& g = *GImGui;
3404 if (window->Flags & ImGuiWindowFlags_Tooltip)
3405 AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window);
3406 else
3407 AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window);
3408 }
3409
FlattenIntoSingleLayer()3410 void ImDrawDataBuilder::FlattenIntoSingleLayer()
3411 {
3412 int n = Layers[0].Size;
3413 int size = n;
3414 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++)
3415 size += Layers[i].Size;
3416 Layers[0].resize(size);
3417 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++)
3418 {
3419 ImVector<ImDrawList*>& layer = Layers[layer_n];
3420 if (layer.empty())
3421 continue;
3422 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*));
3423 n += layer.Size;
3424 layer.resize(0);
3425 }
3426 }
3427
SetupDrawData(ImVector<ImDrawList * > * draw_lists,ImDrawData * draw_data)3428 static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data)
3429 {
3430 ImGuiIO& io = ImGui::GetIO();
3431 draw_data->Valid = true;
3432 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL;
3433 draw_data->CmdListsCount = draw_lists->Size;
3434 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
3435 draw_data->DisplayPos = ImVec2(0.0f, 0.0f);
3436 draw_data->DisplaySize = io.DisplaySize;
3437 for (int n = 0; n < draw_lists->Size; n++)
3438 {
3439 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size;
3440 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size;
3441 }
3442 }
3443
3444 // 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)3445 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
3446 {
3447 ImGuiWindow* window = GetCurrentWindow();
3448 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
3449 window->ClipRect = window->DrawList->_ClipRectStack.back();
3450 }
3451
PopClipRect()3452 void ImGui::PopClipRect()
3453 {
3454 ImGuiWindow* window = GetCurrentWindow();
3455 window->DrawList->PopClipRect();
3456 window->ClipRect = window->DrawList->_ClipRectStack.back();
3457 }
3458
3459 // 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()3460 void ImGui::EndFrame()
3461 {
3462 ImGuiContext& g = *GImGui;
3463 IM_ASSERT(g.Initialized);
3464 if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times.
3465 return;
3466 IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()");
3467
3468 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
3469 if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)
3470 {
3471 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y);
3472 g.PlatformImeLastPos = g.PlatformImePos;
3473 }
3474
3475 // Hide implicit "Debug" window if it hasn't been used
3476 IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name?
3477 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
3478 g.CurrentWindow->Active = false;
3479 End();
3480
3481 // Show CTRL+TAB list
3482 if (g.NavWindowingTarget)
3483 NavUpdateWindowingList();
3484
3485 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
3486 if (g.DragDropActive)
3487 {
3488 bool is_delivered = g.DragDropPayload.Delivery;
3489 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));
3490 if (is_delivered || is_elapsed)
3491 ClearDragDrop();
3492 }
3493
3494 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.
3495 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount)
3496 {
3497 g.DragDropWithinSourceOrTarget = true;
3498 SetTooltip("...");
3499 g.DragDropWithinSourceOrTarget = false;
3500 }
3501
3502 // Initiate moving window
3503 if (g.ActiveId == 0 && g.HoveredId == 0)
3504 {
3505 if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear
3506 {
3507 // Click to focus window and start moving (after we're done with all our widgets)
3508 if (g.IO.MouseClicked[0])
3509 {
3510 if (g.HoveredRootWindow != NULL)
3511 StartMouseMovingWindow(g.HoveredWindow);
3512 else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL)
3513 FocusWindow(NULL); // Clicking on void disable focus
3514 }
3515
3516 // With right mouse button we close popups without changing focus
3517 // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger)
3518 if (g.IO.MouseClicked[1])
3519 {
3520 // Find the top-most window between HoveredWindow and the front most Modal Window.
3521 // This is where we can trim the popup stack.
3522 ImGuiWindow* modal = GetFrontMostPopupModal();
3523 bool hovered_window_above_modal = false;
3524 if (modal == NULL)
3525 hovered_window_above_modal = true;
3526 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--)
3527 {
3528 ImGuiWindow* window = g.Windows[i];
3529 if (window == modal)
3530 break;
3531 if (window == g.HoveredWindow)
3532 hovered_window_above_modal = true;
3533 }
3534 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal);
3535 }
3536 }
3537 }
3538
3539 // Sort the window list so that all child windows are after their parent
3540 // We cannot do that on FocusWindow() because childs may not exist yet
3541 g.WindowsSortBuffer.resize(0);
3542 g.WindowsSortBuffer.reserve(g.Windows.Size);
3543 for (int i = 0; i != g.Windows.Size; i++)
3544 {
3545 ImGuiWindow* window = g.Windows[i];
3546 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it
3547 continue;
3548 AddWindowToSortedBuffer(&g.WindowsSortBuffer, window);
3549 }
3550
3551 IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong
3552 g.Windows.swap(g.WindowsSortBuffer);
3553 g.IO.MetricsActiveWindows = g.WindowsActiveCount;
3554
3555 // Unlock font atlas
3556 g.IO.Fonts->Locked = false;
3557
3558 // Clear Input data for next frame
3559 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
3560 memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters));
3561 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs));
3562
3563 g.FrameScopeActive = false;
3564 g.FrameCountEnded = g.FrameCount;
3565 }
3566
Render()3567 void ImGui::Render()
3568 {
3569 ImGuiContext& g = *GImGui;
3570 IM_ASSERT(g.Initialized);
3571
3572 if (g.FrameCountEnded != g.FrameCount)
3573 ImGui::EndFrame();
3574 g.FrameCountRendered = g.FrameCount;
3575
3576 // Gather ImDrawList to render (for each active window)
3577 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0;
3578 g.DrawDataBuilder.Clear();
3579 ImGuiWindow* windows_to_render_front_most[2];
3580 windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
3581 windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL;
3582 for (int n = 0; n != g.Windows.Size; n++)
3583 {
3584 ImGuiWindow* window = g.Windows[n];
3585 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1])
3586 AddWindowToDrawDataSelectLayer(window);
3587 }
3588 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++)
3589 if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window
3590 AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]);
3591 g.DrawDataBuilder.FlattenIntoSingleLayer();
3592
3593 // Draw software mouse cursor if requested
3594 if (g.IO.MouseDrawCursor)
3595 RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor);
3596
3597 if (!g.OverlayDrawList.VtxBuffer.empty())
3598 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList);
3599
3600 // Setup ImDrawData structure for end-user
3601 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData);
3602 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount;
3603 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount;
3604
3605 // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData()
3606 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3607 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL)
3608 g.IO.RenderDrawListsFn(&g.DrawData);
3609 #endif
3610 }
3611
3612 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
3613 // 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)3614 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
3615 {
3616 ImGuiContext& g = *GImGui;
3617
3618 const char* text_display_end;
3619 if (hide_text_after_double_hash)
3620 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string
3621 else
3622 text_display_end = text_end;
3623
3624 ImFont* font = g.Font;
3625 const float font_size = g.FontSize;
3626 if (text == text_display_end)
3627 return ImVec2(0.0f, font_size);
3628 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
3629
3630 // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field)
3631 const float font_scale = font_size / font->FontSize;
3632 const float character_spacing_x = 1.0f * font_scale;
3633 if (text_size.x > 0.0f)
3634 text_size.x -= character_spacing_x;
3635 text_size.x = (float)(int)(text_size.x + 0.95f);
3636
3637 return text_size;
3638 }
3639
3640 // Helper to calculate coarse clipping of large list of evenly sized items.
3641 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern.
3642 // 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)3643 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
3644 {
3645 ImGuiContext& g = *GImGui;
3646 ImGuiWindow* window = g.CurrentWindow;
3647 if (g.LogEnabled)
3648 {
3649 // If logging is active, do not perform any clipping
3650 *out_items_display_start = 0;
3651 *out_items_display_end = items_count;
3652 return;
3653 }
3654 if (window->SkipItems)
3655 {
3656 *out_items_display_start = *out_items_display_end = 0;
3657 return;
3658 }
3659
3660 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect
3661 ImRect unclipped_rect = window->ClipRect;
3662 if (g.NavMoveRequest)
3663 unclipped_rect.Add(g.NavScoringRectScreen);
3664
3665 const ImVec2 pos = window->DC.CursorPos;
3666 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height);
3667 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height);
3668
3669 // When performing a navigation request, ensure we have one item extra in the direction we are moving to
3670 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up)
3671 start--;
3672 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down)
3673 end++;
3674
3675 start = ImClamp(start, 0, items_count);
3676 end = ImClamp(end + 1, start, items_count);
3677 *out_items_display_start = start;
3678 *out_items_display_end = end;
3679 }
3680
3681 // Find window given position, search front-to-back
3682 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically
3683 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
3684 // called, aka before the next Begin(). Moving window isn't affected.
FindHoveredWindow()3685 static void FindHoveredWindow()
3686 {
3687 ImGuiContext& g = *GImGui;
3688
3689 ImGuiWindow* hovered_window = NULL;
3690 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs))
3691 hovered_window = g.MovingWindow;
3692
3693 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--)
3694 {
3695 ImGuiWindow* window = g.Windows[i];
3696 if (!window->Active || window->Hidden)
3697 continue;
3698 if (window->Flags & ImGuiWindowFlags_NoInputs)
3699 continue;
3700
3701 // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
3702 ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding);
3703 if (bb.Contains(g.IO.MousePos))
3704 {
3705 if (hovered_window == NULL)
3706 hovered_window = window;
3707 if (hovered_window)
3708 break;
3709 }
3710 }
3711
3712 g.HoveredWindow = hovered_window;
3713 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
3714
3715 }
3716
3717 // Test if mouse cursor is hovering given rectangle
3718 // NB- Rectangle is clipped by our current clip setting
3719 // 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)3720 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
3721 {
3722 ImGuiContext& g = *GImGui;
3723
3724 // Clip
3725 ImRect rect_clipped(r_min, r_max);
3726 if (clip)
3727 rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
3728
3729 // Expand for touch input
3730 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding);
3731 if (!rect_for_touch.Contains(g.IO.MousePos))
3732 return false;
3733 return true;
3734 }
3735
GetKeyIndex(ImGuiKey imgui_key)3736 int ImGui::GetKeyIndex(ImGuiKey imgui_key)
3737 {
3738 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
3739 return GImGui->IO.KeyMap[imgui_key];
3740 }
3741
3742 // 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)3743 bool ImGui::IsKeyDown(int user_key_index)
3744 {
3745 if (user_key_index < 0) return false;
3746 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown));
3747 return GImGui->IO.KeysDown[user_key_index];
3748 }
3749
CalcTypematicPressedRepeatAmount(float t,float t_prev,float repeat_delay,float repeat_rate)3750 int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate)
3751 {
3752 if (t == 0.0f)
3753 return 1;
3754 if (t <= repeat_delay || repeat_rate <= 0.0f)
3755 return 0;
3756 const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate);
3757 return (count > 0) ? count : 0;
3758 }
3759
GetKeyPressedAmount(int key_index,float repeat_delay,float repeat_rate)3760 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
3761 {
3762 ImGuiContext& g = *GImGui;
3763 if (key_index < 0) return false;
3764 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3765 const float t = g.IO.KeysDownDuration[key_index];
3766 return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate);
3767 }
3768
IsKeyPressed(int user_key_index,bool repeat)3769 bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
3770 {
3771 ImGuiContext& g = *GImGui;
3772 if (user_key_index < 0) return false;
3773 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3774 const float t = g.IO.KeysDownDuration[user_key_index];
3775 if (t == 0.0f)
3776 return true;
3777 if (repeat && t > g.IO.KeyRepeatDelay)
3778 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
3779 return false;
3780 }
3781
IsKeyReleased(int user_key_index)3782 bool ImGui::IsKeyReleased(int user_key_index)
3783 {
3784 ImGuiContext& g = *GImGui;
3785 if (user_key_index < 0) return false;
3786 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
3787 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
3788 }
3789
IsMouseDown(int button)3790 bool ImGui::IsMouseDown(int button)
3791 {
3792 ImGuiContext& g = *GImGui;
3793 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3794 return g.IO.MouseDown[button];
3795 }
3796
IsAnyMouseDown()3797 bool ImGui::IsAnyMouseDown()
3798 {
3799 ImGuiContext& g = *GImGui;
3800 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
3801 if (g.IO.MouseDown[n])
3802 return true;
3803 return false;
3804 }
3805
IsMouseClicked(int button,bool repeat)3806 bool ImGui::IsMouseClicked(int button, bool repeat)
3807 {
3808 ImGuiContext& g = *GImGui;
3809 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3810 const float t = g.IO.MouseDownDuration[button];
3811 if (t == 0.0f)
3812 return true;
3813
3814 if (repeat && t > g.IO.KeyRepeatDelay)
3815 {
3816 float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate;
3817 if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f))
3818 return true;
3819 }
3820
3821 return false;
3822 }
3823
IsMouseReleased(int button)3824 bool ImGui::IsMouseReleased(int button)
3825 {
3826 ImGuiContext& g = *GImGui;
3827 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3828 return g.IO.MouseReleased[button];
3829 }
3830
IsMouseDoubleClicked(int button)3831 bool ImGui::IsMouseDoubleClicked(int button)
3832 {
3833 ImGuiContext& g = *GImGui;
3834 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3835 return g.IO.MouseDoubleClicked[button];
3836 }
3837
IsMouseDragging(int button,float lock_threshold)3838 bool ImGui::IsMouseDragging(int button, float lock_threshold)
3839 {
3840 ImGuiContext& g = *GImGui;
3841 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3842 if (!g.IO.MouseDown[button])
3843 return false;
3844 if (lock_threshold < 0.0f)
3845 lock_threshold = g.IO.MouseDragThreshold;
3846 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
3847 }
3848
GetMousePos()3849 ImVec2 ImGui::GetMousePos()
3850 {
3851 return GImGui->IO.MousePos;
3852 }
3853
3854 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
GetMousePosOnOpeningCurrentPopup()3855 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
3856 {
3857 ImGuiContext& g = *GImGui;
3858 if (g.CurrentPopupStack.Size > 0)
3859 return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos;
3860 return g.IO.MousePos;
3861 }
3862
3863 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position
IsMousePosValid(const ImVec2 * mouse_pos)3864 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
3865 {
3866 if (mouse_pos == NULL)
3867 mouse_pos = &GImGui->IO.MousePos;
3868 const float MOUSE_INVALID = -256000.0f;
3869 return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID;
3870 }
3871
3872 // 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)3873 ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold)
3874 {
3875 ImGuiContext& g = *GImGui;
3876 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3877 if (lock_threshold < 0.0f)
3878 lock_threshold = g.IO.MouseDragThreshold;
3879 if (g.IO.MouseDown[button])
3880 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
3881 return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment).
3882 return ImVec2(0.0f, 0.0f);
3883 }
3884
ResetMouseDragDelta(int button)3885 void ImGui::ResetMouseDragDelta(int button)
3886 {
3887 ImGuiContext& g = *GImGui;
3888 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
3889 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
3890 g.IO.MouseClickedPos[button] = g.IO.MousePos;
3891 }
3892
GetMouseCursor()3893 ImGuiMouseCursor ImGui::GetMouseCursor()
3894 {
3895 return GImGui->MouseCursor;
3896 }
3897
SetMouseCursor(ImGuiMouseCursor cursor_type)3898 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
3899 {
3900 GImGui->MouseCursor = cursor_type;
3901 }
3902
CaptureKeyboardFromApp(bool capture)3903 void ImGui::CaptureKeyboardFromApp(bool capture)
3904 {
3905 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0;
3906 }
3907
CaptureMouseFromApp(bool capture)3908 void ImGui::CaptureMouseFromApp(bool capture)
3909 {
3910 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0;
3911 }
3912
IsItemActive()3913 bool ImGui::IsItemActive()
3914 {
3915 ImGuiContext& g = *GImGui;
3916 if (g.ActiveId)
3917 {
3918 ImGuiWindow* window = g.CurrentWindow;
3919 return g.ActiveId == window->DC.LastItemId;
3920 }
3921 return false;
3922 }
3923
IsItemDeactivated()3924 bool ImGui::IsItemDeactivated()
3925 {
3926 ImGuiContext& g = *GImGui;
3927 ImGuiWindow* window = g.CurrentWindow;
3928 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId);
3929 }
3930
IsItemDeactivatedAfterEdit()3931 bool ImGui::IsItemDeactivatedAfterEdit()
3932 {
3933 ImGuiContext& g = *GImGui;
3934 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEdited || (g.ActiveId == 0 && g.ActiveIdHasBeenEdited));
3935 }
3936
IsItemFocused()3937 bool ImGui::IsItemFocused()
3938 {
3939 ImGuiContext& g = *GImGui;
3940 return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
3941 }
3942
IsItemClicked(int mouse_button)3943 bool ImGui::IsItemClicked(int mouse_button)
3944 {
3945 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
3946 }
3947
IsAnyItemHovered()3948 bool ImGui::IsAnyItemHovered()
3949 {
3950 ImGuiContext& g = *GImGui;
3951 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
3952 }
3953
IsAnyItemActive()3954 bool ImGui::IsAnyItemActive()
3955 {
3956 ImGuiContext& g = *GImGui;
3957 return g.ActiveId != 0;
3958 }
3959
IsAnyItemFocused()3960 bool ImGui::IsAnyItemFocused()
3961 {
3962 ImGuiContext& g = *GImGui;
3963 return g.NavId != 0 && !g.NavDisableHighlight;
3964 }
3965
IsItemVisible()3966 bool ImGui::IsItemVisible()
3967 {
3968 ImGuiWindow* window = GetCurrentWindowRead();
3969 return window->ClipRect.Overlaps(window->DC.LastItemRect);
3970 }
3971
IsItemEdited()3972 bool ImGui::IsItemEdited()
3973 {
3974 ImGuiWindow* window = GetCurrentWindowRead();
3975 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0;
3976 }
3977
3978 // 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()3979 void ImGui::SetItemAllowOverlap()
3980 {
3981 ImGuiContext& g = *GImGui;
3982 if (g.HoveredId == g.CurrentWindow->DC.LastItemId)
3983 g.HoveredIdAllowOverlap = true;
3984 if (g.ActiveId == g.CurrentWindow->DC.LastItemId)
3985 g.ActiveIdAllowOverlap = true;
3986 }
3987
GetItemRectMin()3988 ImVec2 ImGui::GetItemRectMin()
3989 {
3990 ImGuiWindow* window = GetCurrentWindowRead();
3991 return window->DC.LastItemRect.Min;
3992 }
3993
GetItemRectMax()3994 ImVec2 ImGui::GetItemRectMax()
3995 {
3996 ImGuiWindow* window = GetCurrentWindowRead();
3997 return window->DC.LastItemRect.Max;
3998 }
3999
GetItemRectSize()4000 ImVec2 ImGui::GetItemRectSize()
4001 {
4002 ImGuiWindow* window = GetCurrentWindowRead();
4003 return window->DC.LastItemRect.GetSize();
4004 }
4005
GetViewportRect()4006 static ImRect GetViewportRect()
4007 {
4008 ImGuiContext& g = *GImGui;
4009 if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y)
4010 return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax);
4011 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y);
4012 }
4013
BeginChildEx(const char * name,ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags flags)4014 static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags)
4015 {
4016 ImGuiContext& g = *GImGui;
4017 ImGuiWindow* parent_window = g.CurrentWindow;
4018
4019 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
4020 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
4021
4022 // Size
4023 const ImVec2 content_avail = GetContentRegionAvail();
4024 ImVec2 size = ImFloor(size_arg);
4025 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00);
4026 if (size.x <= 0.0f)
4027 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues)
4028 if (size.y <= 0.0f)
4029 size.y = ImMax(content_avail.y + size.y, 4.0f);
4030 SetNextWindowSize(size);
4031
4032 // Name
4033 char title[256];
4034 if (name)
4035 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name);
4036 else
4037 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id);
4038
4039 const float backup_border_size = g.Style.ChildBorderSize;
4040 if (!border)
4041 g.Style.ChildBorderSize = 0.0f;
4042 bool ret = Begin(title, NULL, flags);
4043 g.Style.ChildBorderSize = backup_border_size;
4044
4045 ImGuiWindow* child_window = g.CurrentWindow;
4046 child_window->ChildId = id;
4047 child_window->AutoFitChildAxises = auto_fit_axises;
4048
4049 // Process navigation-in immediately so NavInit can run on first frame
4050 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll))
4051 {
4052 FocusWindow(child_window);
4053 NavInitWindow(child_window, false);
4054 SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item
4055 g.ActiveIdSource = ImGuiInputSource_Nav;
4056 }
4057 return ret;
4058 }
4059
BeginChild(const char * str_id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4060 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4061 {
4062 ImGuiWindow* window = GetCurrentWindow();
4063 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags);
4064 }
4065
BeginChild(ImGuiID id,const ImVec2 & size_arg,bool border,ImGuiWindowFlags extra_flags)4066 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags)
4067 {
4068 IM_ASSERT(id != 0);
4069 return BeginChildEx(NULL, id, size_arg, border, extra_flags);
4070 }
4071
EndChild()4072 void ImGui::EndChild()
4073 {
4074 ImGuiContext& g = *GImGui;
4075 ImGuiWindow* window = g.CurrentWindow;
4076
4077 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss
4078 if (window->BeginCount > 1)
4079 {
4080 End();
4081 }
4082 else
4083 {
4084 ImVec2 sz = window->Size;
4085 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f
4086 sz.x = ImMax(4.0f, sz.x);
4087 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y))
4088 sz.y = ImMax(4.0f, sz.y);
4089 End();
4090
4091 ImGuiWindow* parent_window = g.CurrentWindow;
4092 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz);
4093 ItemSize(sz);
4094 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened))
4095 {
4096 ItemAdd(bb, window->ChildId);
4097 RenderNavHighlight(bb, window->ChildId);
4098
4099 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child
4100 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow)
4101 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);
4102 }
4103 else
4104 {
4105 // Not navigable into
4106 ItemAdd(bb, 0);
4107 }
4108 }
4109 }
4110
4111 // Helper to create a child window / scrolling region that looks like a normal widget frame.
BeginChildFrame(ImGuiID id,const ImVec2 & size,ImGuiWindowFlags extra_flags)4112 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags)
4113 {
4114 ImGuiContext& g = *GImGui;
4115 const ImGuiStyle& style = g.Style;
4116 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4117 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4118 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4119 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
4120 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags);
4121 PopStyleVar(3);
4122 PopStyleColor();
4123 return ret;
4124 }
4125
EndChildFrame()4126 void ImGui::EndChildFrame()
4127 {
4128 EndChild();
4129 }
4130
4131 // Save and compare stack sizes on Begin()/End() to detect usage errors
CheckStacksSize(ImGuiWindow * window,bool write)4132 static void CheckStacksSize(ImGuiWindow* window, bool write)
4133 {
4134 // 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)
4135 ImGuiContext& g = *GImGui;
4136 int* p_backup = &window->DC.StackSizesBackup[0];
4137 { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop()
4138 { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup()
4139 { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup()
4140 // 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.
4141 { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor()
4142 { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar()
4143 { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont()
4144 IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup));
4145 }
4146
SetWindowConditionAllowFlags(ImGuiWindow * window,ImGuiCond flags,bool enabled)4147 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
4148 {
4149 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
4150 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
4151 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
4152 }
4153
FindWindowByName(const char * name)4154 ImGuiWindow* ImGui::FindWindowByName(const char* name)
4155 {
4156 ImGuiContext& g = *GImGui;
4157 ImGuiID id = ImHash(name, 0);
4158 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
4159 }
4160
CreateNewWindow(const char * name,ImVec2 size,ImGuiWindowFlags flags)4161 static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags)
4162 {
4163 ImGuiContext& g = *GImGui;
4164
4165 // Create window the first time
4166 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
4167 window->Flags = flags;
4168 g.WindowsById.SetVoidPtr(window->ID, window);
4169
4170 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
4171 window->Pos = ImVec2(60, 60);
4172
4173 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings.
4174 if (!(flags & ImGuiWindowFlags_NoSavedSettings))
4175 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID))
4176 {
4177 // Retrieve settings from .ini file
4178 window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
4179 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
4180 window->Pos = ImFloor(settings->Pos);
4181 window->Collapsed = settings->Collapsed;
4182 if (ImLengthSqr(settings->Size) > 0.00001f)
4183 size = ImFloor(settings->Size);
4184 }
4185 window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
4186 window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
4187
4188 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
4189 {
4190 window->AutoFitFramesX = window->AutoFitFramesY = 2;
4191 window->AutoFitOnlyGrows = false;
4192 }
4193 else
4194 {
4195 if (window->Size.x <= 0.0f)
4196 window->AutoFitFramesX = 2;
4197 if (window->Size.y <= 0.0f)
4198 window->AutoFitFramesY = 2;
4199 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
4200 }
4201
4202 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
4203 g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once
4204 else
4205 g.Windows.push_back(window);
4206 return window;
4207 }
4208
CalcSizeAfterConstraint(ImGuiWindow * window,ImVec2 new_size)4209 static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
4210 {
4211 ImGuiContext& g = *GImGui;
4212 if (g.NextWindowData.SizeConstraintCond != 0)
4213 {
4214 // Using -1,-1 on either X/Y axis to preserve the current size.
4215 ImRect cr = g.NextWindowData.SizeConstraintRect;
4216 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
4217 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
4218 if (g.NextWindowData.SizeCallback)
4219 {
4220 ImGuiSizeCallbackData data;
4221 data.UserData = g.NextWindowData.SizeCallbackUserData;
4222 data.Pos = window->Pos;
4223 data.CurrentSize = window->SizeFull;
4224 data.DesiredSize = new_size;
4225 g.NextWindowData.SizeCallback(&data);
4226 new_size = data.DesiredSize;
4227 }
4228 }
4229
4230 // Minimum size
4231 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
4232 {
4233 new_size = ImMax(new_size, g.Style.WindowMinSize);
4234 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
4235 }
4236 return new_size;
4237 }
4238
CalcSizeContents(ImGuiWindow * window)4239 static ImVec2 CalcSizeContents(ImGuiWindow* window)
4240 {
4241 ImVec2 sz;
4242 sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x));
4243 sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y));
4244 return sz + window->WindowPadding;
4245 }
4246
CalcSizeAutoFit(ImGuiWindow * window,const ImVec2 & size_contents)4247 static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents)
4248 {
4249 ImGuiContext& g = *GImGui;
4250 ImGuiStyle& style = g.Style;
4251 if (window->Flags & ImGuiWindowFlags_Tooltip)
4252 {
4253 // Tooltip always resize
4254 return size_contents;
4255 }
4256 else
4257 {
4258 // 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.
4259 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0;
4260 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0;
4261 ImVec2 size_min = style.WindowMinSize;
4262 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)
4263 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f));
4264 ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f));
4265 ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit);
4266 if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar))
4267 size_auto_fit.y += style.ScrollbarSize;
4268 if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar))
4269 size_auto_fit.x += style.ScrollbarSize;
4270 return size_auto_fit;
4271 }
4272 }
4273
CalcWindowExpectedSize(ImGuiWindow * window)4274 ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window)
4275 {
4276 ImVec2 size_contents = CalcSizeContents(window);
4277 return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents));
4278 }
4279
GetScrollMaxX(ImGuiWindow * window)4280 static float GetScrollMaxX(ImGuiWindow* window)
4281 {
4282 return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x));
4283 }
4284
GetScrollMaxY(ImGuiWindow * window)4285 static float GetScrollMaxY(ImGuiWindow* window)
4286 {
4287 return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y));
4288 }
4289
CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow * window,bool snap_on_edges)4290 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges)
4291 {
4292 ImGuiContext& g = *GImGui;
4293 ImVec2 scroll = window->Scroll;
4294 if (window->ScrollTarget.x < FLT_MAX)
4295 {
4296 float cr_x = window->ScrollTargetCenterRatio.x;
4297 scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x);
4298 }
4299 if (window->ScrollTarget.y < FLT_MAX)
4300 {
4301 // '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.
4302 float cr_y = window->ScrollTargetCenterRatio.y;
4303 float target_y = window->ScrollTarget.y;
4304 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y)
4305 target_y = 0.0f;
4306 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y)
4307 target_y = window->SizeContents.y;
4308 scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y);
4309 }
4310 scroll = ImMax(scroll, ImVec2(0.0f, 0.0f));
4311 if (!window->Collapsed && !window->SkipItems)
4312 {
4313 scroll.x = ImMin(scroll.x, GetScrollMaxX(window));
4314 scroll.y = ImMin(scroll.y, GetScrollMaxY(window));
4315 }
4316 return scroll;
4317 }
4318
GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)4319 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags)
4320 {
4321 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
4322 return ImGuiCol_PopupBg;
4323 if (flags & ImGuiWindowFlags_ChildWindow)
4324 return ImGuiCol_ChildBg;
4325 return ImGuiCol_WindowBg;
4326 }
4327
CalcResizePosSizeFromAnyCorner(ImGuiWindow * window,const ImVec2 & corner_target,const ImVec2 & corner_norm,ImVec2 * out_pos,ImVec2 * out_size)4328 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
4329 {
4330 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left
4331 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
4332 ImVec2 size_expected = pos_max - pos_min;
4333 ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected);
4334 *out_pos = pos_min;
4335 if (corner_norm.x == 0.0f)
4336 out_pos->x -= (size_constrained.x - size_expected.x);
4337 if (corner_norm.y == 0.0f)
4338 out_pos->y -= (size_constrained.y - size_expected.y);
4339 *out_size = size_constrained;
4340 }
4341
4342 struct ImGuiResizeGripDef
4343 {
4344 ImVec2 CornerPos;
4345 ImVec2 InnerDir;
4346 int AngleMin12, AngleMax12;
4347 };
4348
4349 const ImGuiResizeGripDef resize_grip_def[4] =
4350 {
4351 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right
4352 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left
4353 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left
4354 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right
4355 };
4356
GetResizeBorderRect(ImGuiWindow * window,int border_n,float perp_padding,float thickness)4357 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
4358 {
4359 ImRect rect = window->Rect();
4360 if (thickness == 0.0f) rect.Max -= ImVec2(1,1);
4361 if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness);
4362 if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding);
4363 if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y);
4364 if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding);
4365 IM_ASSERT(0);
4366 return ImRect();
4367 }
4368
4369 // 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])4370 static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4])
4371 {
4372 ImGuiContext& g = *GImGui;
4373 ImGuiWindowFlags flags = window->Flags;
4374 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4375 return;
4376
4377 const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0;
4378 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4379 const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f);
4380
4381 ImVec2 pos_target(FLT_MAX, FLT_MAX);
4382 ImVec2 size_target(FLT_MAX, FLT_MAX);
4383
4384 // Manual resize grips
4385 PushID("#RESIZE");
4386 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4387 {
4388 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4389 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4390
4391 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
4392 ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size);
4393 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
4394 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
4395 bool hovered, held;
4396 ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
4397 if (hovered || held)
4398 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;
4399
4400 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0)
4401 {
4402 // Manual auto-fit when double-clicking
4403 size_target = CalcSizeAfterConstraint(window, size_auto_fit);
4404 ClearActiveID();
4405 }
4406 else if (held)
4407 {
4408 // Resize from any of the four corners
4409 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
4410 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip
4411 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target);
4412 }
4413 if (resize_grip_n == 0 || held || hovered)
4414 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
4415 }
4416 for (int border_n = 0; border_n < resize_border_count; border_n++)
4417 {
4418 const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check.
4419 const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise
4420 bool hovered, held;
4421 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_size, BORDER_SIZE);
4422 ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);
4423 if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held)
4424 {
4425 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;
4426 if (held) *border_held = border_n;
4427 }
4428 if (held)
4429 {
4430 ImVec2 border_target = window->Pos;
4431 ImVec2 border_posn;
4432 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); }
4433 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); }
4434 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); }
4435 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); }
4436 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target);
4437 }
4438 }
4439 PopID();
4440
4441 // Navigation resize (keyboard/gamepad)
4442 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
4443 {
4444 ImVec2 nav_resize_delta;
4445 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift)
4446 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
4447 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
4448 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down);
4449 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f)
4450 {
4451 const float NAV_RESIZE_SPEED = 600.0f;
4452 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y));
4453 g.NavWindowingToggleLayer = false;
4454 g.NavDisableMouseHover = true;
4455 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
4456 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
4457 size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta);
4458 }
4459 }
4460
4461 // Apply back modified position/size to window
4462 if (size_target.x != FLT_MAX)
4463 {
4464 window->SizeFull = size_target;
4465 MarkIniSettingsDirty(window);
4466 }
4467 if (pos_target.x != FLT_MAX)
4468 {
4469 window->Pos = ImFloor(pos_target);
4470 MarkIniSettingsDirty(window);
4471 }
4472
4473 window->Size = window->SizeFull;
4474 }
4475
UpdateWindowParentAndRootLinks(ImGuiWindow * window,ImGuiWindowFlags flags,ImGuiWindow * parent_window)4476 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
4477 {
4478 window->ParentWindow = parent_window;
4479 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
4480 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
4481 window->RootWindow = parent_window->RootWindow;
4482 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
4483 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
4484 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
4485 window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
4486 }
4487
4488 // Push a new ImGui window to add widgets to.
4489 // - 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.
4490 // - Begin/End can be called multiple times during the frame with the same window name to append content.
4491 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
4492 // 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.
4493 // - 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.
4494 // - 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)4495 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
4496 {
4497 ImGuiContext& g = *GImGui;
4498 const ImGuiStyle& style = g.Style;
4499 IM_ASSERT(name != NULL); // Window name required
4500 IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame()
4501 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
4502
4503 // Find or create
4504 ImGuiWindow* window = FindWindowByName(name);
4505 const bool window_just_created = (window == NULL);
4506 if (window_just_created)
4507 {
4508 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.
4509 window = CreateNewWindow(name, size_on_first_use, flags);
4510 }
4511
4512 // Automatically disable manual moving/resizing when NoInputs is set
4513 if (flags & ImGuiWindowFlags_NoInputs)
4514 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
4515
4516 if (flags & ImGuiWindowFlags_NavFlattened)
4517 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);
4518
4519 const int current_frame = g.FrameCount;
4520 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
4521 if (first_begin_of_the_frame)
4522 window->Flags = (ImGuiWindowFlags)flags;
4523 else
4524 flags = window->Flags;
4525
4526 // 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
4527 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
4528 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
4529 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
4530 window->HasCloseButton = (p_open != NULL);
4531
4532 // Update the Appearing flag
4533 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
4534 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
4535 if (flags & ImGuiWindowFlags_Popup)
4536 {
4537 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4538 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
4539 window_just_activated_by_user |= (window != popup_ref.Window);
4540 }
4541 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize);
4542 if (window->Appearing)
4543 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
4544
4545 // Add to stack
4546 g.CurrentWindowStack.push_back(window);
4547 SetCurrentWindow(window);
4548 CheckStacksSize(window, true);
4549 if (flags & ImGuiWindowFlags_Popup)
4550 {
4551 ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size];
4552 popup_ref.Window = window;
4553 g.CurrentPopupStack.push_back(popup_ref);
4554 window->PopupId = popup_ref.PopupId;
4555 }
4556
4557 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow))
4558 window->NavLastIds[0] = 0;
4559
4560 // Process SetNextWindow***() calls
4561 bool window_pos_set_by_api = false;
4562 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
4563 if (g.NextWindowData.PosCond)
4564 {
4565 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
4566 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
4567 {
4568 // May be processed on the next frame if this is our first frame and we are measuring size
4569 // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
4570 window->SetWindowPosVal = g.NextWindowData.PosVal;
4571 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
4572 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
4573 }
4574 else
4575 {
4576 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
4577 }
4578 }
4579 if (g.NextWindowData.SizeCond)
4580 {
4581 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
4582 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
4583 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
4584 }
4585 if (g.NextWindowData.ContentSizeCond)
4586 {
4587 // Adjust passed "client size" to become a "window size"
4588 window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal;
4589 if (window->SizeContentsExplicit.y != 0.0f)
4590 window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight();
4591 }
4592 else if (first_begin_of_the_frame)
4593 {
4594 window->SizeContentsExplicit = ImVec2(0.0f, 0.0f);
4595 }
4596 if (g.NextWindowData.CollapsedCond)
4597 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
4598 if (g.NextWindowData.FocusCond)
4599 FocusWindow(window);
4600 if (window->Appearing)
4601 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
4602
4603 // When reusing window again multiple times a frame, just append content (don't need to setup again)
4604 if (first_begin_of_the_frame)
4605 {
4606 // Initialize
4607 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
4608 UpdateWindowParentAndRootLinks(window, flags, parent_window);
4609
4610 window->Active = true;
4611 window->BeginOrderWithinParent = 0;
4612 window->BeginOrderWithinContext = g.WindowsActiveCount++;
4613 window->BeginCount = 0;
4614 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
4615 window->LastFrameActive = current_frame;
4616 window->IDStack.resize(1);
4617
4618 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
4619
4620 // Update contents size from last frame for auto-fitting (or use explicit size)
4621 window->SizeContents = CalcSizeContents(window);
4622 if (window->HiddenFramesRegular > 0)
4623 window->HiddenFramesRegular--;
4624 if (window->HiddenFramesForResize > 0)
4625 window->HiddenFramesForResize--;
4626
4627 // Hide new windows for one frame until they calculate their size
4628 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
4629 window->HiddenFramesForResize = 1;
4630
4631 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
4632 // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
4633 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
4634 {
4635 window->HiddenFramesForResize = 1;
4636 if (flags & ImGuiWindowFlags_AlwaysAutoResize)
4637 {
4638 if (!window_size_x_set_by_api)
4639 window->Size.x = window->SizeFull.x = 0.f;
4640 if (!window_size_y_set_by_api)
4641 window->Size.y = window->SizeFull.y = 0.f;
4642 window->SizeContents = ImVec2(0.f, 0.f);
4643 }
4644 }
4645
4646 SetCurrentWindow(window);
4647
4648 // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
4649 window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
4650 window->WindowPadding = style.WindowPadding;
4651 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
4652 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
4653 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
4654 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
4655
4656 // Collapse window by double-clicking on title bar
4657 // 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
4658 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
4659 {
4660 // 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.
4661 ImRect title_bar_rect = window->TitleBarRect();
4662 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])
4663 window->WantCollapseToggle = true;
4664 if (window->WantCollapseToggle)
4665 {
4666 window->Collapsed = !window->Collapsed;
4667 MarkIniSettingsDirty(window);
4668 FocusWindow(window);
4669 }
4670 }
4671 else
4672 {
4673 window->Collapsed = false;
4674 }
4675 window->WantCollapseToggle = false;
4676
4677 // SIZE
4678
4679 // Calculate auto-fit size, handle automatic resize
4680 const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents);
4681 ImVec2 size_full_modified(FLT_MAX, FLT_MAX);
4682 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
4683 {
4684 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
4685 if (!window_size_x_set_by_api)
4686 window->SizeFull.x = size_full_modified.x = size_auto_fit.x;
4687 if (!window_size_y_set_by_api)
4688 window->SizeFull.y = size_full_modified.y = size_auto_fit.y;
4689 }
4690 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
4691 {
4692 // Auto-fit may only grow window during the first few frames
4693 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
4694 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
4695 window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
4696 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
4697 window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
4698 if (!window->Collapsed)
4699 MarkIniSettingsDirty(window);
4700 }
4701
4702 // Apply minimum/maximum window size constraints and final size
4703 window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
4704 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
4705
4706 // SCROLLBAR STATUS
4707
4708 // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size).
4709 if (!window->Collapsed)
4710 {
4711 // When reading the current size we need to read it after size constraints have been applied
4712 float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x;
4713 float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y;
4714 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
4715 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
4716 if (window->ScrollbarX && !window->ScrollbarY)
4717 window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
4718 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
4719 }
4720
4721 // POSITION
4722
4723 // Popup latch its initial position, will position itself when it appears next frame
4724 if (window_just_activated_by_user)
4725 {
4726 window->AutoPosLastDirection = ImGuiDir_None;
4727 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api)
4728 window->Pos = g.CurrentPopupStack.back().OpenPopupPos;
4729 }
4730
4731 // Position child window
4732 if (flags & ImGuiWindowFlags_ChildWindow)
4733 {
4734 window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size;
4735 parent_window->DC.ChildWindows.push_back(window);
4736 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
4737 window->Pos = parent_window->DC.CursorPos;
4738 }
4739
4740 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesForResize == 0);
4741 if (window_pos_with_pivot)
4742 SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering)
4743 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
4744 window->Pos = FindBestWindowPosForPopup(window);
4745 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
4746 window->Pos = FindBestWindowPosForPopup(window);
4747 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
4748 window->Pos = FindBestWindowPosForPopup(window);
4749
4750 // Clamp position so it stays visible
4751 if (!(flags & ImGuiWindowFlags_ChildWindow))
4752 {
4753 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.
4754 {
4755 ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
4756 window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size;
4757 window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
4758 }
4759 }
4760 window->Pos = ImFloor(window->Pos);
4761
4762 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
4763 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
4764
4765 // Prepare for item focus requests
4766 window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1);
4767 window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1);
4768 window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1;
4769 window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX;
4770
4771 // Apply scrolling
4772 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true);
4773 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4774
4775 // Apply window focus (new and reactivated windows are moved to front)
4776 bool want_focus = false;
4777 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
4778 if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
4779 want_focus = true;
4780
4781 // Handle manual resize: Resize Grips, Borders, Gamepad
4782 int border_held = -1;
4783 ImU32 resize_grip_col[4] = { 0 };
4784 const int resize_grip_count = g.IO.ConfigResizeWindowsFromEdges ? 2 : 1; // 4
4785 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f);
4786 if (!window->Collapsed)
4787 UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]);
4788
4789 // Default item width. Make it proportional to window size if window manually resizes
4790 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
4791 window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f);
4792 else
4793 window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f);
4794
4795 // DRAWING
4796
4797 // Setup draw list and outer clipping rectangle
4798 window->DrawList->Clear();
4799 window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0);
4800 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);
4801 ImRect viewport_rect(GetViewportRect());
4802 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
4803 PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true);
4804 else
4805 PushClipRect(viewport_rect.Min, viewport_rect.Max, true);
4806
4807 // Draw modal window background (darkens what is behind them, all viewports)
4808 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFramesForResize <= 0;
4809 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow);
4810 if (dim_bg_for_modal || dim_bg_for_window_list)
4811 {
4812 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio);
4813 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col);
4814 }
4815
4816 // Draw navigation selection/windowing rectangle background
4817 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim)
4818 {
4819 ImRect bb = window->Rect();
4820 bb.Expand(g.FontSize);
4821 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway
4822 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding);
4823 }
4824
4825 // Draw window + handle manual resize
4826 const float window_rounding = window->WindowRounding;
4827 const float window_border_size = window->WindowBorderSize;
4828 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
4829 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
4830 const ImRect title_bar_rect = window->TitleBarRect();
4831 if (window->Collapsed)
4832 {
4833 // Title bar only
4834 float backup_border_size = style.FrameBorderSize;
4835 g.Style.FrameBorderSize = window->WindowBorderSize;
4836 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
4837 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
4838 g.Style.FrameBorderSize = backup_border_size;
4839 }
4840 else
4841 {
4842 // Window background
4843 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
4844 if (g.NextWindowData.BgAlphaCond != 0)
4845 {
4846 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
4847 g.NextWindowData.BgAlphaCond = 0;
4848 }
4849 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
4850
4851 // Title bar
4852 ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
4853 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4854 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
4855
4856 // Menu bar
4857 if (flags & ImGuiWindowFlags_MenuBar)
4858 {
4859 ImRect menu_bar_rect = window->MenuBarRect();
4860 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.
4861 window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top);
4862 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
4863 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4864 }
4865
4866 // Scrollbars
4867 if (window->ScrollbarX)
4868 Scrollbar(ImGuiLayoutType_Horizontal);
4869 if (window->ScrollbarY)
4870 Scrollbar(ImGuiLayoutType_Vertical);
4871
4872 // Render resize grips (after their input handling so we don't have a frame of latency)
4873 if (!(flags & ImGuiWindowFlags_NoResize))
4874 {
4875 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
4876 {
4877 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
4878 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos);
4879 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size)));
4880 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size)));
4881 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);
4882 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]);
4883 }
4884 }
4885
4886 // Borders
4887 if (window_border_size > 0.0f)
4888 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size);
4889 if (border_held != -1)
4890 {
4891 ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f);
4892 window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
4893 }
4894 if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
4895 window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
4896 }
4897
4898 // Draw navigation selection/windowing rectangle border
4899 if (g.NavWindowingTargetAnim == window)
4900 {
4901 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
4902 ImRect bb = window->Rect();
4903 bb.Expand(g.FontSize);
4904 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward
4905 {
4906 bb.Expand(-g.FontSize - 1.0f);
4907 rounding = window->WindowRounding;
4908 }
4909 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
4910 }
4911
4912 // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars.
4913 window->SizeFullAtLastBegin = window->SizeFull;
4914
4915 // Update various regions. Variables they depends on are set above in this function.
4916 // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
4917 window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x;
4918 window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight();
4919 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));
4920 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));
4921
4922 // Setup drawing context
4923 // (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.)
4924 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x;
4925 window->DC.GroupOffset.x = 0.0f;
4926 window->DC.ColumnsOffset.x = 0.0f;
4927 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y);
4928 window->DC.CursorPos = window->DC.CursorStartPos;
4929 window->DC.CursorPosPrevLine = window->DC.CursorPos;
4930 window->DC.CursorMaxPos = window->DC.CursorStartPos;
4931 window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
4932 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
4933 window->DC.NavHideHighlightOneFrame = false;
4934 window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f);
4935 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext;
4936 window->DC.NavLayerActiveMaskNext = 0x00;
4937 window->DC.MenuBarAppending = false;
4938 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f;
4939 window->DC.ChildWindows.resize(0);
4940 window->DC.LayoutType = ImGuiLayoutType_Vertical;
4941 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
4942 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_;
4943 window->DC.ItemWidth = window->ItemWidthDefault;
4944 window->DC.TextWrapPos = -1.0f; // disabled
4945 window->DC.ItemFlagsStack.resize(0);
4946 window->DC.ItemWidthStack.resize(0);
4947 window->DC.TextWrapPosStack.resize(0);
4948 window->DC.ColumnsSet = NULL;
4949 window->DC.TreeDepth = 0;
4950 window->DC.TreeDepthMayJumpToParentOnPop = 0x00;
4951 window->DC.StateStorage = &window->StateStorage;
4952 window->DC.GroupStack.resize(0);
4953 window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user);
4954
4955 if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags))
4956 {
4957 window->DC.ItemFlags = parent_window->DC.ItemFlags;
4958 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
4959 }
4960
4961 if (window->AutoFitFramesX > 0)
4962 window->AutoFitFramesX--;
4963 if (window->AutoFitFramesY > 0)
4964 window->AutoFitFramesY--;
4965
4966 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
4967 if (want_focus)
4968 {
4969 FocusWindow(window);
4970 NavInitWindow(window, false);
4971 }
4972
4973 // Title bar
4974 if (!(flags & ImGuiWindowFlags_NoTitleBar))
4975 {
4976 // Close & collapse button are on layer 1 (same as menus) and don't default focus
4977 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
4978 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
4979 window->DC.NavLayerCurrent++;
4980 window->DC.NavLayerCurrentMask <<= 1;
4981
4982 // Collapse button
4983 if (!(flags & ImGuiWindowFlags_NoCollapse))
4984 if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos))
4985 window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function
4986
4987 // Close button
4988 if (p_open != NULL)
4989 {
4990 const float pad = style.FramePadding.y;
4991 const float rad = g.FontSize * 0.5f;
4992 if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
4993 *p_open = false;
4994 }
4995
4996 window->DC.NavLayerCurrent--;
4997 window->DC.NavLayerCurrentMask >>= 1;
4998 window->DC.ItemFlags = item_flags_backup;
4999
5000 // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.)
5001 ImVec2 text_size = CalcTextSize(name, NULL, true);
5002 ImRect text_r = title_bar_rect;
5003 float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5004 float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x);
5005 if (style.WindowTitleAlign.x > 0.0f)
5006 pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x);
5007 text_r.Min.x += pad_left;
5008 text_r.Max.x -= pad_right;
5009 ImRect clip_rect = text_r;
5010 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()
5011 RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect);
5012 }
5013
5014 // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
5015 window->OuterRectClipped = window->Rect();
5016 window->OuterRectClipped.ClipWith(window->ClipRect);
5017
5018 // Pressing CTRL+C while holding on a window copy its content to the clipboard
5019 // 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.
5020 // Maybe we can support CTRL+C on every element?
5021 /*
5022 if (g.ActiveId == move_id)
5023 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
5024 ImGui::LogToClipboard();
5025 */
5026
5027 // Inner rectangle
5028 // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame
5029 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
5030 window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize;
5031 window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
5032 window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize;
5033 window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize;
5034 //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE);
5035
5036 // Inner clipping rectangle
5037 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
5038 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5039 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y);
5040 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
5041 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
5042
5043 // 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.).
5044 window->DC.LastItemId = window->MoveId;
5045 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
5046 window->DC.LastItemRect = title_bar_rect;
5047 }
5048
5049 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
5050
5051 // 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)
5052 if (first_begin_of_the_frame)
5053 window->WriteAccessed = false;
5054
5055 window->BeginCount++;
5056 g.NextWindowData.Clear();
5057
5058 if (flags & ImGuiWindowFlags_ChildWindow)
5059 {
5060 // Child window can be out of sight and have "negative" clip windows.
5061 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
5062 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
5063
5064 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
5065 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
5066 window->HiddenFramesRegular = 1;
5067
5068 // Completely hide along with parent or if parent is collapsed
5069 if (parent_window && (parent_window->Collapsed || parent_window->Hidden))
5070 window->HiddenFramesRegular = 1;
5071 }
5072
5073 // 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)
5074 if (style.Alpha <= 0.0f)
5075 window->HiddenFramesRegular = 1;
5076
5077 // Update the Hidden flag
5078 window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize);
5079
5080 // Return false if we don't intend to display anything to allow user to perform an early out optimization
5081 window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
5082
5083 return !window->SkipItems;
5084 }
5085
5086 // Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead.
5087 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
Begin(const char * name,bool * p_open,const ImVec2 & size_first_use,float bg_alpha_override,ImGuiWindowFlags flags)5088 bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags)
5089 {
5090 // 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.
5091 if (size_first_use.x != 0.0f || size_first_use.y != 0.0f)
5092 ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver);
5093
5094 // Old API feature: override the window background alpha with a parameter.
5095 if (bg_alpha_override >= 0.0f)
5096 ImGui::SetNextWindowBgAlpha(bg_alpha_override);
5097
5098 return ImGui::Begin(name, p_open, flags);
5099 }
5100 #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5101
End()5102 void ImGui::End()
5103 {
5104 ImGuiContext& g = *GImGui;
5105 ImGuiWindow* window = g.CurrentWindow;
5106
5107 if (window->DC.ColumnsSet != NULL)
5108 EndColumns();
5109 PopClipRect(); // Inner window clip rectangle
5110
5111 // Stop logging
5112 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
5113 LogFinish();
5114
5115 // Pop from window stack
5116 g.CurrentWindowStack.pop_back();
5117 if (window->Flags & ImGuiWindowFlags_Popup)
5118 g.CurrentPopupStack.pop_back();
5119 CheckStacksSize(window, false);
5120 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back());
5121 }
5122
BringWindowToFront(ImGuiWindow * window)5123 void ImGui::BringWindowToFront(ImGuiWindow* window)
5124 {
5125 ImGuiContext& g = *GImGui;
5126 ImGuiWindow* current_front_window = g.Windows.back();
5127 if (current_front_window == window || current_front_window->RootWindow == window)
5128 return;
5129 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window
5130 if (g.Windows[i] == window)
5131 {
5132 g.Windows.erase(g.Windows.Data + i);
5133 g.Windows.push_back(window);
5134 break;
5135 }
5136 }
5137
BringWindowToBack(ImGuiWindow * window)5138 void ImGui::BringWindowToBack(ImGuiWindow* window)
5139 {
5140 ImGuiContext& g = *GImGui;
5141 if (g.Windows[0] == window)
5142 return;
5143 for (int i = 0; i < g.Windows.Size; i++)
5144 if (g.Windows[i] == window)
5145 {
5146 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
5147 g.Windows[0] = window;
5148 break;
5149 }
5150 }
5151
5152 // Moving window to front of display and set focus (which happens to be back of our sorted list)
FocusWindow(ImGuiWindow * window)5153 void ImGui::FocusWindow(ImGuiWindow* window)
5154 {
5155 ImGuiContext& g = *GImGui;
5156
5157 if (g.NavWindow != window)
5158 {
5159 g.NavWindow = window;
5160 if (window && g.NavDisableMouseHover)
5161 g.NavMousePosDirty = true;
5162 g.NavInitRequest = false;
5163 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
5164 g.NavIdIsAlive = false;
5165 g.NavLayer = 0;
5166 //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
5167 }
5168
5169 // Passing NULL allow to disable keyboard focus
5170 if (!window)
5171 return;
5172
5173 // Move the root window to the top of the pile
5174 if (window->RootWindow)
5175 window = window->RootWindow;
5176
5177 // Steal focus on active widgets
5178 if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it..
5179 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window)
5180 ClearActiveID();
5181
5182 // Bring to front
5183 if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus))
5184 BringWindowToFront(window);
5185 }
5186
FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow * ignore_window)5187 void ImGui::FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window)
5188 {
5189 ImGuiContext& g = *GImGui;
5190 for (int i = g.Windows.Size - 1; i >= 0; i--)
5191 if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow))
5192 {
5193 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]);
5194 FocusWindow(focus_window);
5195 return;
5196 }
5197 }
5198
PushItemWidth(float item_width)5199 void ImGui::PushItemWidth(float item_width)
5200 {
5201 ImGuiWindow* window = GetCurrentWindow();
5202 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
5203 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth);
5204 }
5205
PushMultiItemsWidths(int components,float w_full)5206 void ImGui::PushMultiItemsWidths(int components, float w_full)
5207 {
5208 ImGuiWindow* window = GetCurrentWindow();
5209 const ImGuiStyle& style = GImGui->Style;
5210 if (w_full <= 0.0f)
5211 w_full = CalcItemWidth();
5212 const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components));
5213 const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1)));
5214 window->DC.ItemWidthStack.push_back(w_item_last);
5215 for (int i = 0; i < components-1; i++)
5216 window->DC.ItemWidthStack.push_back(w_item_one);
5217 window->DC.ItemWidth = window->DC.ItemWidthStack.back();
5218 }
5219
PopItemWidth()5220 void ImGui::PopItemWidth()
5221 {
5222 ImGuiWindow* window = GetCurrentWindow();
5223 window->DC.ItemWidthStack.pop_back();
5224 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back();
5225 }
5226
CalcItemWidth()5227 float ImGui::CalcItemWidth()
5228 {
5229 ImGuiWindow* window = GetCurrentWindowRead();
5230 float w = window->DC.ItemWidth;
5231 if (w < 0.0f)
5232 {
5233 // 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.
5234 float width_to_right_edge = GetContentRegionAvail().x;
5235 w = ImMax(1.0f, width_to_right_edge + w);
5236 }
5237 w = (float)(int)w;
5238 return w;
5239 }
5240
SetCurrentFont(ImFont * font)5241 void ImGui::SetCurrentFont(ImFont* font)
5242 {
5243 ImGuiContext& g = *GImGui;
5244 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
5245 IM_ASSERT(font->Scale > 0.0f);
5246 g.Font = font;
5247 g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale;
5248 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;
5249
5250 ImFontAtlas* atlas = g.Font->ContainerAtlas;
5251 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
5252 g.DrawListSharedData.Font = g.Font;
5253 g.DrawListSharedData.FontSize = g.FontSize;
5254 }
5255
PushFont(ImFont * font)5256 void ImGui::PushFont(ImFont* font)
5257 {
5258 ImGuiContext& g = *GImGui;
5259 if (!font)
5260 font = GetDefaultFont();
5261 SetCurrentFont(font);
5262 g.FontStack.push_back(font);
5263 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);
5264 }
5265
PopFont()5266 void ImGui::PopFont()
5267 {
5268 ImGuiContext& g = *GImGui;
5269 g.CurrentWindow->DrawList->PopTextureID();
5270 g.FontStack.pop_back();
5271 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());
5272 }
5273
PushItemFlag(ImGuiItemFlags option,bool enabled)5274 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
5275 {
5276 ImGuiWindow* window = GetCurrentWindow();
5277 if (enabled)
5278 window->DC.ItemFlags |= option;
5279 else
5280 window->DC.ItemFlags &= ~option;
5281 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags);
5282 }
5283
PopItemFlag()5284 void ImGui::PopItemFlag()
5285 {
5286 ImGuiWindow* window = GetCurrentWindow();
5287 window->DC.ItemFlagsStack.pop_back();
5288 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back();
5289 }
5290
PushAllowKeyboardFocus(bool allow_keyboard_focus)5291 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus)
5292 {
5293 PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus);
5294 }
5295
PopAllowKeyboardFocus()5296 void ImGui::PopAllowKeyboardFocus()
5297 {
5298 PopItemFlag();
5299 }
5300
PushButtonRepeat(bool repeat)5301 void ImGui::PushButtonRepeat(bool repeat)
5302 {
5303 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);
5304 }
5305
PopButtonRepeat()5306 void ImGui::PopButtonRepeat()
5307 {
5308 PopItemFlag();
5309 }
5310
PushTextWrapPos(float wrap_pos_x)5311 void ImGui::PushTextWrapPos(float wrap_pos_x)
5312 {
5313 ImGuiWindow* window = GetCurrentWindow();
5314 window->DC.TextWrapPos = wrap_pos_x;
5315 window->DC.TextWrapPosStack.push_back(wrap_pos_x);
5316 }
5317
PopTextWrapPos()5318 void ImGui::PopTextWrapPos()
5319 {
5320 ImGuiWindow* window = GetCurrentWindow();
5321 window->DC.TextWrapPosStack.pop_back();
5322 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back();
5323 }
5324
5325 // 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)5326 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
5327 {
5328 ImGuiContext& g = *GImGui;
5329 ImGuiColorMod backup;
5330 backup.Col = idx;
5331 backup.BackupValue = g.Style.Colors[idx];
5332 g.ColorModifiers.push_back(backup);
5333 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
5334 }
5335
PushStyleColor(ImGuiCol idx,const ImVec4 & col)5336 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
5337 {
5338 ImGuiContext& g = *GImGui;
5339 ImGuiColorMod backup;
5340 backup.Col = idx;
5341 backup.BackupValue = g.Style.Colors[idx];
5342 g.ColorModifiers.push_back(backup);
5343 g.Style.Colors[idx] = col;
5344 }
5345
PopStyleColor(int count)5346 void ImGui::PopStyleColor(int count)
5347 {
5348 ImGuiContext& g = *GImGui;
5349 while (count > 0)
5350 {
5351 ImGuiColorMod& backup = g.ColorModifiers.back();
5352 g.Style.Colors[backup.Col] = backup.BackupValue;
5353 g.ColorModifiers.pop_back();
5354 count--;
5355 }
5356 }
5357
5358 struct ImGuiStyleVarInfo
5359 {
5360 ImGuiDataType Type;
5361 ImU32 Count;
5362 ImU32 Offset;
GetVarPtrImGuiStyleVarInfo5363 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); }
5364 };
5365
5366 static const ImGuiStyleVarInfo GStyleVarInfo[] =
5367 {
5368 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha
5369 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding
5370 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding
5371 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize
5372 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize
5373 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign
5374 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding
5375 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize
5376 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding
5377 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize
5378 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding
5379 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding
5380 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize
5381 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing
5382 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing
5383 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing
5384 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize
5385 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding
5386 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize
5387 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding
5388 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
5389 };
5390
GetStyleVarInfo(ImGuiStyleVar idx)5391 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx)
5392 {
5393 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
5394 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);
5395 return &GStyleVarInfo[idx];
5396 }
5397
PushStyleVar(ImGuiStyleVar idx,float val)5398 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
5399 {
5400 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5401 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)
5402 {
5403 ImGuiContext& g = *GImGui;
5404 float* pvar = (float*)var_info->GetVarPtr(&g.Style);
5405 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5406 *pvar = val;
5407 return;
5408 }
5409 IM_ASSERT(0); // Called function with wrong-type? Variable is not a float.
5410 }
5411
PushStyleVar(ImGuiStyleVar idx,const ImVec2 & val)5412 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
5413 {
5414 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
5415 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)
5416 {
5417 ImGuiContext& g = *GImGui;
5418 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
5419 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar));
5420 *pvar = val;
5421 return;
5422 }
5423 IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2.
5424 }
5425
PopStyleVar(int count)5426 void ImGui::PopStyleVar(int count)
5427 {
5428 ImGuiContext& g = *GImGui;
5429 while (count > 0)
5430 {
5431 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
5432 ImGuiStyleMod& backup = g.StyleModifiers.back();
5433 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx);
5434 void* data = info->GetVarPtr(&g.Style);
5435 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; }
5436 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
5437 g.StyleModifiers.pop_back();
5438 count--;
5439 }
5440 }
5441
GetStyleColorName(ImGuiCol idx)5442 const char* ImGui::GetStyleColorName(ImGuiCol idx)
5443 {
5444 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
5445 switch (idx)
5446 {
5447 case ImGuiCol_Text: return "Text";
5448 case ImGuiCol_TextDisabled: return "TextDisabled";
5449 case ImGuiCol_WindowBg: return "WindowBg";
5450 case ImGuiCol_ChildBg: return "ChildBg";
5451 case ImGuiCol_PopupBg: return "PopupBg";
5452 case ImGuiCol_Border: return "Border";
5453 case ImGuiCol_BorderShadow: return "BorderShadow";
5454 case ImGuiCol_FrameBg: return "FrameBg";
5455 case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
5456 case ImGuiCol_FrameBgActive: return "FrameBgActive";
5457 case ImGuiCol_TitleBg: return "TitleBg";
5458 case ImGuiCol_TitleBgActive: return "TitleBgActive";
5459 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
5460 case ImGuiCol_MenuBarBg: return "MenuBarBg";
5461 case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
5462 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
5463 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
5464 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
5465 case ImGuiCol_CheckMark: return "CheckMark";
5466 case ImGuiCol_SliderGrab: return "SliderGrab";
5467 case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
5468 case ImGuiCol_Button: return "Button";
5469 case ImGuiCol_ButtonHovered: return "ButtonHovered";
5470 case ImGuiCol_ButtonActive: return "ButtonActive";
5471 case ImGuiCol_Header: return "Header";
5472 case ImGuiCol_HeaderHovered: return "HeaderHovered";
5473 case ImGuiCol_HeaderActive: return "HeaderActive";
5474 case ImGuiCol_Separator: return "Separator";
5475 case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
5476 case ImGuiCol_SeparatorActive: return "SeparatorActive";
5477 case ImGuiCol_ResizeGrip: return "ResizeGrip";
5478 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
5479 case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
5480 case ImGuiCol_PlotLines: return "PlotLines";
5481 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
5482 case ImGuiCol_PlotHistogram: return "PlotHistogram";
5483 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
5484 case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
5485 case ImGuiCol_DragDropTarget: return "DragDropTarget";
5486 case ImGuiCol_NavHighlight: return "NavHighlight";
5487 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
5488 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
5489 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
5490 }
5491 IM_ASSERT(0);
5492 return "Unknown";
5493 }
5494
IsWindowChildOf(ImGuiWindow * window,ImGuiWindow * potential_parent)5495 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
5496 {
5497 if (window->RootWindow == potential_parent)
5498 return true;
5499 while (window != NULL)
5500 {
5501 if (window == potential_parent)
5502 return true;
5503 window = window->ParentWindow;
5504 }
5505 return false;
5506 }
5507
IsWindowHovered(ImGuiHoveredFlags flags)5508 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
5509 {
5510 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function
5511 ImGuiContext& g = *GImGui;
5512
5513 if (flags & ImGuiHoveredFlags_AnyWindow)
5514 {
5515 if (g.HoveredWindow == NULL)
5516 return false;
5517 }
5518 else
5519 {
5520 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
5521 {
5522 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
5523 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
5524 return false;
5525 break;
5526 case ImGuiHoveredFlags_RootWindow:
5527 if (g.HoveredWindow != g.CurrentWindow->RootWindow)
5528 return false;
5529 break;
5530 case ImGuiHoveredFlags_ChildWindows:
5531 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow))
5532 return false;
5533 break;
5534 default:
5535 if (g.HoveredWindow != g.CurrentWindow)
5536 return false;
5537 break;
5538 }
5539 }
5540
5541 if (!IsWindowContentHoverable(g.HoveredRootWindow, flags))
5542 return false;
5543 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
5544 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId)
5545 return false;
5546 return true;
5547 }
5548
IsWindowFocused(ImGuiFocusedFlags flags)5549 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
5550 {
5551 ImGuiContext& g = *GImGui;
5552 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End()
5553
5554 if (flags & ImGuiFocusedFlags_AnyWindow)
5555 return g.NavWindow != NULL;
5556
5557 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
5558 {
5559 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
5560 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
5561 case ImGuiFocusedFlags_RootWindow:
5562 return g.NavWindow == g.CurrentWindow->RootWindow;
5563 case ImGuiFocusedFlags_ChildWindows:
5564 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
5565 default:
5566 return g.NavWindow == g.CurrentWindow;
5567 }
5568 }
5569
5570 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
IsWindowNavFocusable(ImGuiWindow * window)5571 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
5572 {
5573 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
5574 }
5575
GetWindowWidth()5576 float ImGui::GetWindowWidth()
5577 {
5578 ImGuiWindow* window = GImGui->CurrentWindow;
5579 return window->Size.x;
5580 }
5581
GetWindowHeight()5582 float ImGui::GetWindowHeight()
5583 {
5584 ImGuiWindow* window = GImGui->CurrentWindow;
5585 return window->Size.y;
5586 }
5587
GetWindowPos()5588 ImVec2 ImGui::GetWindowPos()
5589 {
5590 ImGuiContext& g = *GImGui;
5591 ImGuiWindow* window = g.CurrentWindow;
5592 return window->Pos;
5593 }
5594
SetWindowScrollX(ImGuiWindow * window,float new_scroll_x)5595 void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
5596 {
5597 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.
5598 window->Scroll.x = new_scroll_x;
5599 window->DC.CursorMaxPos.x -= window->Scroll.x;
5600 }
5601
SetWindowScrollY(ImGuiWindow * window,float new_scroll_y)5602 void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
5603 {
5604 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.
5605 window->Scroll.y = new_scroll_y;
5606 window->DC.CursorMaxPos.y -= window->Scroll.y;
5607 }
5608
SetWindowPos(ImGuiWindow * window,const ImVec2 & pos,ImGuiCond cond)5609 static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
5610 {
5611 // Test condition (NB: bit 0 is always true) and clear flags for next time
5612 if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
5613 return;
5614
5615 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5616 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5617 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
5618
5619 // Set
5620 const ImVec2 old_pos = window->Pos;
5621 window->Pos = ImFloor(pos);
5622 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
5623 window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected.
5624 }
5625
SetWindowPos(const ImVec2 & pos,ImGuiCond cond)5626 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
5627 {
5628 ImGuiWindow* window = GetCurrentWindowRead();
5629 SetWindowPos(window, pos, cond);
5630 }
5631
SetWindowPos(const char * name,const ImVec2 & pos,ImGuiCond cond)5632 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
5633 {
5634 if (ImGuiWindow* window = FindWindowByName(name))
5635 SetWindowPos(window, pos, cond);
5636 }
5637
GetWindowSize()5638 ImVec2 ImGui::GetWindowSize()
5639 {
5640 ImGuiWindow* window = GetCurrentWindowRead();
5641 return window->Size;
5642 }
5643
SetWindowSize(ImGuiWindow * window,const ImVec2 & size,ImGuiCond cond)5644 static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
5645 {
5646 // Test condition (NB: bit 0 is always true) and clear flags for next time
5647 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
5648 return;
5649
5650 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5651 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5652
5653 // Set
5654 if (size.x > 0.0f)
5655 {
5656 window->AutoFitFramesX = 0;
5657 window->SizeFull.x = size.x;
5658 }
5659 else
5660 {
5661 window->AutoFitFramesX = 2;
5662 window->AutoFitOnlyGrows = false;
5663 }
5664 if (size.y > 0.0f)
5665 {
5666 window->AutoFitFramesY = 0;
5667 window->SizeFull.y = size.y;
5668 }
5669 else
5670 {
5671 window->AutoFitFramesY = 2;
5672 window->AutoFitOnlyGrows = false;
5673 }
5674 }
5675
SetWindowSize(const ImVec2 & size,ImGuiCond cond)5676 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
5677 {
5678 SetWindowSize(GImGui->CurrentWindow, size, cond);
5679 }
5680
SetWindowSize(const char * name,const ImVec2 & size,ImGuiCond cond)5681 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
5682 {
5683 if (ImGuiWindow* window = FindWindowByName(name))
5684 SetWindowSize(window, size, cond);
5685 }
5686
SetWindowCollapsed(ImGuiWindow * window,bool collapsed,ImGuiCond cond)5687 static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
5688 {
5689 // Test condition (NB: bit 0 is always true) and clear flags for next time
5690 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
5691 return;
5692 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
5693
5694 // Set
5695 window->Collapsed = collapsed;
5696 }
5697
SetWindowCollapsed(bool collapsed,ImGuiCond cond)5698 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
5699 {
5700 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
5701 }
5702
IsWindowCollapsed()5703 bool ImGui::IsWindowCollapsed()
5704 {
5705 ImGuiWindow* window = GetCurrentWindowRead();
5706 return window->Collapsed;
5707 }
5708
IsWindowAppearing()5709 bool ImGui::IsWindowAppearing()
5710 {
5711 ImGuiWindow* window = GetCurrentWindowRead();
5712 return window->Appearing;
5713 }
5714
SetWindowCollapsed(const char * name,bool collapsed,ImGuiCond cond)5715 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
5716 {
5717 if (ImGuiWindow* window = FindWindowByName(name))
5718 SetWindowCollapsed(window, collapsed, cond);
5719 }
5720
SetWindowFocus()5721 void ImGui::SetWindowFocus()
5722 {
5723 FocusWindow(GImGui->CurrentWindow);
5724 }
5725
SetWindowFocus(const char * name)5726 void ImGui::SetWindowFocus(const char* name)
5727 {
5728 if (name)
5729 {
5730 if (ImGuiWindow* window = FindWindowByName(name))
5731 FocusWindow(window);
5732 }
5733 else
5734 {
5735 FocusWindow(NULL);
5736 }
5737 }
5738
SetNextWindowPos(const ImVec2 & pos,ImGuiCond cond,const ImVec2 & pivot)5739 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
5740 {
5741 ImGuiContext& g = *GImGui;
5742 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5743 g.NextWindowData.PosVal = pos;
5744 g.NextWindowData.PosPivotVal = pivot;
5745 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
5746 }
5747
SetNextWindowSize(const ImVec2 & size,ImGuiCond cond)5748 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
5749 {
5750 ImGuiContext& g = *GImGui;
5751 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5752 g.NextWindowData.SizeVal = size;
5753 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
5754 }
5755
SetNextWindowSizeConstraints(const ImVec2 & size_min,const ImVec2 & size_max,ImGuiSizeCallback custom_callback,void * custom_callback_user_data)5756 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
5757 {
5758 ImGuiContext& g = *GImGui;
5759 g.NextWindowData.SizeConstraintCond = ImGuiCond_Always;
5760 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
5761 g.NextWindowData.SizeCallback = custom_callback;
5762 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
5763 }
5764
SetNextWindowContentSize(const ImVec2 & size)5765 void ImGui::SetNextWindowContentSize(const ImVec2& size)
5766 {
5767 ImGuiContext& g = *GImGui;
5768 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.
5769 g.NextWindowData.ContentSizeCond = ImGuiCond_Always;
5770 }
5771
SetNextWindowCollapsed(bool collapsed,ImGuiCond cond)5772 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
5773 {
5774 ImGuiContext& g = *GImGui;
5775 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
5776 g.NextWindowData.CollapsedVal = collapsed;
5777 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
5778 }
5779
SetNextWindowFocus()5780 void ImGui::SetNextWindowFocus()
5781 {
5782 ImGuiContext& g = *GImGui;
5783 g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5784 }
5785
SetNextWindowBgAlpha(float alpha)5786 void ImGui::SetNextWindowBgAlpha(float alpha)
5787 {
5788 ImGuiContext& g = *GImGui;
5789 g.NextWindowData.BgAlphaVal = alpha;
5790 g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
5791 }
5792
5793 // In window space (not screen space!)
GetContentRegionMax()5794 ImVec2 ImGui::GetContentRegionMax()
5795 {
5796 ImGuiWindow* window = GetCurrentWindowRead();
5797 ImVec2 mx = window->ContentsRegionRect.Max - window->Pos;
5798 if (window->DC.ColumnsSet)
5799 mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x;
5800 return mx;
5801 }
5802
GetContentRegionAvail()5803 ImVec2 ImGui::GetContentRegionAvail()
5804 {
5805 ImGuiWindow* window = GetCurrentWindowRead();
5806 return GetContentRegionMax() - (window->DC.CursorPos - window->Pos);
5807 }
5808
GetContentRegionAvailWidth()5809 float ImGui::GetContentRegionAvailWidth()
5810 {
5811 return GetContentRegionAvail().x;
5812 }
5813
5814 // In window space (not screen space!)
GetWindowContentRegionMin()5815 ImVec2 ImGui::GetWindowContentRegionMin()
5816 {
5817 ImGuiWindow* window = GetCurrentWindowRead();
5818 return window->ContentsRegionRect.Min - window->Pos;
5819 }
5820
GetWindowContentRegionMax()5821 ImVec2 ImGui::GetWindowContentRegionMax()
5822 {
5823 ImGuiWindow* window = GetCurrentWindowRead();
5824 return window->ContentsRegionRect.Max - window->Pos;
5825 }
5826
GetWindowContentRegionWidth()5827 float ImGui::GetWindowContentRegionWidth()
5828 {
5829 ImGuiWindow* window = GetCurrentWindowRead();
5830 return window->ContentsRegionRect.GetWidth();
5831 }
5832
GetTextLineHeight()5833 float ImGui::GetTextLineHeight()
5834 {
5835 ImGuiContext& g = *GImGui;
5836 return g.FontSize;
5837 }
5838
GetTextLineHeightWithSpacing()5839 float ImGui::GetTextLineHeightWithSpacing()
5840 {
5841 ImGuiContext& g = *GImGui;
5842 return g.FontSize + g.Style.ItemSpacing.y;
5843 }
5844
GetFrameHeight()5845 float ImGui::GetFrameHeight()
5846 {
5847 ImGuiContext& g = *GImGui;
5848 return g.FontSize + g.Style.FramePadding.y * 2.0f;
5849 }
5850
GetFrameHeightWithSpacing()5851 float ImGui::GetFrameHeightWithSpacing()
5852 {
5853 ImGuiContext& g = *GImGui;
5854 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
5855 }
5856
GetWindowDrawList()5857 ImDrawList* ImGui::GetWindowDrawList()
5858 {
5859 ImGuiWindow* window = GetCurrentWindow();
5860 return window->DrawList;
5861 }
5862
GetFont()5863 ImFont* ImGui::GetFont()
5864 {
5865 return GImGui->Font;
5866 }
5867
GetFontSize()5868 float ImGui::GetFontSize()
5869 {
5870 return GImGui->FontSize;
5871 }
5872
GetFontTexUvWhitePixel()5873 ImVec2 ImGui::GetFontTexUvWhitePixel()
5874 {
5875 return GImGui->DrawListSharedData.TexUvWhitePixel;
5876 }
5877
SetWindowFontScale(float scale)5878 void ImGui::SetWindowFontScale(float scale)
5879 {
5880 ImGuiContext& g = *GImGui;
5881 ImGuiWindow* window = GetCurrentWindow();
5882 window->FontWindowScale = scale;
5883 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();
5884 }
5885
5886 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
5887 // 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()5888 ImVec2 ImGui::GetCursorPos()
5889 {
5890 ImGuiWindow* window = GetCurrentWindowRead();
5891 return window->DC.CursorPos - window->Pos + window->Scroll;
5892 }
5893
GetCursorPosX()5894 float ImGui::GetCursorPosX()
5895 {
5896 ImGuiWindow* window = GetCurrentWindowRead();
5897 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
5898 }
5899
GetCursorPosY()5900 float ImGui::GetCursorPosY()
5901 {
5902 ImGuiWindow* window = GetCurrentWindowRead();
5903 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
5904 }
5905
SetCursorPos(const ImVec2 & local_pos)5906 void ImGui::SetCursorPos(const ImVec2& local_pos)
5907 {
5908 ImGuiWindow* window = GetCurrentWindow();
5909 window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
5910 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5911 }
5912
SetCursorPosX(float x)5913 void ImGui::SetCursorPosX(float x)
5914 {
5915 ImGuiWindow* window = GetCurrentWindow();
5916 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
5917 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
5918 }
5919
SetCursorPosY(float y)5920 void ImGui::SetCursorPosY(float y)
5921 {
5922 ImGuiWindow* window = GetCurrentWindow();
5923 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
5924 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
5925 }
5926
GetCursorStartPos()5927 ImVec2 ImGui::GetCursorStartPos()
5928 {
5929 ImGuiWindow* window = GetCurrentWindowRead();
5930 return window->DC.CursorStartPos - window->Pos;
5931 }
5932
GetCursorScreenPos()5933 ImVec2 ImGui::GetCursorScreenPos()
5934 {
5935 ImGuiWindow* window = GetCurrentWindowRead();
5936 return window->DC.CursorPos;
5937 }
5938
SetCursorScreenPos(const ImVec2 & screen_pos)5939 void ImGui::SetCursorScreenPos(const ImVec2& screen_pos)
5940 {
5941 ImGuiWindow* window = GetCurrentWindow();
5942 window->DC.CursorPos = screen_pos;
5943 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
5944 }
5945
GetScrollX()5946 float ImGui::GetScrollX()
5947 {
5948 return GImGui->CurrentWindow->Scroll.x;
5949 }
5950
GetScrollY()5951 float ImGui::GetScrollY()
5952 {
5953 return GImGui->CurrentWindow->Scroll.y;
5954 }
5955
GetScrollMaxX()5956 float ImGui::GetScrollMaxX()
5957 {
5958 return GetScrollMaxX(GImGui->CurrentWindow);
5959 }
5960
GetScrollMaxY()5961 float ImGui::GetScrollMaxY()
5962 {
5963 return GetScrollMaxY(GImGui->CurrentWindow);
5964 }
5965
SetScrollX(float scroll_x)5966 void ImGui::SetScrollX(float scroll_x)
5967 {
5968 ImGuiWindow* window = GetCurrentWindow();
5969 window->ScrollTarget.x = scroll_x;
5970 window->ScrollTargetCenterRatio.x = 0.0f;
5971 }
5972
SetScrollY(float scroll_y)5973 void ImGui::SetScrollY(float scroll_y)
5974 {
5975 ImGuiWindow* window = GetCurrentWindow();
5976 window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY
5977 window->ScrollTargetCenterRatio.y = 0.0f;
5978 }
5979
SetScrollFromPosY(float pos_y,float center_y_ratio)5980 void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio)
5981 {
5982 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
5983 ImGuiWindow* window = GetCurrentWindow();
5984 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
5985 window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y);
5986 window->ScrollTargetCenterRatio.y = center_y_ratio;
5987 }
5988
5989 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
SetScrollHere(float center_y_ratio)5990 void ImGui::SetScrollHere(float center_y_ratio)
5991 {
5992 ImGuiWindow* window = GetCurrentWindow();
5993 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space
5994 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.
5995 SetScrollFromPosY(target_y, center_y_ratio);
5996 }
5997
ActivateItem(ImGuiID id)5998 void ImGui::ActivateItem(ImGuiID id)
5999 {
6000 ImGuiContext& g = *GImGui;
6001 g.NavNextActivateId = id;
6002 }
6003
SetKeyboardFocusHere(int offset)6004 void ImGui::SetKeyboardFocusHere(int offset)
6005 {
6006 IM_ASSERT(offset >= -1); // -1 is allowed but not below
6007 ImGuiWindow* window = GetCurrentWindow();
6008 window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset;
6009 window->FocusIdxTabRequestNext = INT_MAX;
6010 }
6011
SetItemDefaultFocus()6012 void ImGui::SetItemDefaultFocus()
6013 {
6014 ImGuiContext& g = *GImGui;
6015 ImGuiWindow* window = g.CurrentWindow;
6016 if (!window->Appearing)
6017 return;
6018 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent)
6019 {
6020 g.NavInitRequest = false;
6021 g.NavInitResultId = g.NavWindow->DC.LastItemId;
6022 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos);
6023 NavUpdateAnyRequestFlag();
6024 if (!IsItemVisible())
6025 SetScrollHere();
6026 }
6027 }
6028
SetStateStorage(ImGuiStorage * tree)6029 void ImGui::SetStateStorage(ImGuiStorage* tree)
6030 {
6031 ImGuiWindow* window = GetCurrentWindow();
6032 window->DC.StateStorage = tree ? tree : &window->StateStorage;
6033 }
6034
GetStateStorage()6035 ImGuiStorage* ImGui::GetStateStorage()
6036 {
6037 ImGuiWindow* window = GetCurrentWindowRead();
6038 return window->DC.StateStorage;
6039 }
6040
PushID(const char * str_id)6041 void ImGui::PushID(const char* str_id)
6042 {
6043 ImGuiWindow* window = GetCurrentWindowRead();
6044 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id));
6045 }
6046
PushID(const char * str_id_begin,const char * str_id_end)6047 void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
6048 {
6049 ImGuiWindow* window = GetCurrentWindowRead();
6050 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end));
6051 }
6052
PushID(const void * ptr_id)6053 void ImGui::PushID(const void* ptr_id)
6054 {
6055 ImGuiWindow* window = GetCurrentWindowRead();
6056 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6057 }
6058
PushID(int int_id)6059 void ImGui::PushID(int int_id)
6060 {
6061 const void* ptr_id = (void*)(intptr_t)int_id;
6062 ImGuiWindow* window = GetCurrentWindowRead();
6063 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id));
6064 }
6065
PopID()6066 void ImGui::PopID()
6067 {
6068 ImGuiWindow* window = GetCurrentWindowRead();
6069 window->IDStack.pop_back();
6070 }
6071
GetID(const char * str_id)6072 ImGuiID ImGui::GetID(const char* str_id)
6073 {
6074 return GImGui->CurrentWindow->GetID(str_id);
6075 }
6076
GetID(const char * str_id_begin,const char * str_id_end)6077 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
6078 {
6079 return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end);
6080 }
6081
GetID(const void * ptr_id)6082 ImGuiID ImGui::GetID(const void* ptr_id)
6083 {
6084 return GImGui->CurrentWindow->GetID(ptr_id);
6085 }
6086
IsRectVisible(const ImVec2 & size)6087 bool ImGui::IsRectVisible(const ImVec2& size)
6088 {
6089 ImGuiWindow* window = GetCurrentWindowRead();
6090 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
6091 }
6092
IsRectVisible(const ImVec2 & rect_min,const ImVec2 & rect_max)6093 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
6094 {
6095 ImGuiWindow* window = GetCurrentWindowRead();
6096 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
6097 }
6098
6099 // 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()6100 void ImGui::BeginGroup()
6101 {
6102 ImGuiContext& g = *GImGui;
6103 ImGuiWindow* window = GetCurrentWindow();
6104
6105 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1);
6106 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6107 group_data.BackupCursorPos = window->DC.CursorPos;
6108 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
6109 group_data.BackupIndent = window->DC.Indent;
6110 group_data.BackupGroupOffset = window->DC.GroupOffset;
6111 group_data.BackupCurrentLineSize = window->DC.CurrentLineSize;
6112 group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset;
6113 group_data.BackupLogLinePosY = window->DC.LogLinePosY;
6114 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
6115 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;
6116 group_data.AdvanceCursor = true;
6117
6118 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
6119 window->DC.Indent = window->DC.GroupOffset;
6120 window->DC.CursorMaxPos = window->DC.CursorPos;
6121 window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
6122 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6123 }
6124
EndGroup()6125 void ImGui::EndGroup()
6126 {
6127 ImGuiContext& g = *GImGui;
6128 ImGuiWindow* window = GetCurrentWindow();
6129 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls
6130
6131 ImGuiGroupData& group_data = window->DC.GroupStack.back();
6132
6133 ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos);
6134 group_bb.Max = ImMax(group_bb.Min, group_bb.Max);
6135
6136 window->DC.CursorPos = group_data.BackupCursorPos;
6137 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);
6138 window->DC.Indent = group_data.BackupIndent;
6139 window->DC.GroupOffset = group_data.BackupGroupOffset;
6140 window->DC.CurrentLineSize = group_data.BackupCurrentLineSize;
6141 window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset;
6142 window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return
6143
6144 if (group_data.AdvanceCursor)
6145 {
6146 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.
6147 ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset);
6148 ItemAdd(group_bb, 0);
6149 }
6150
6151 // 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.
6152 // 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.
6153 // (and if you grep for LastItemId you'll notice it is only used in that context.
6154 if ((group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow)
6155 window->DC.LastItemId = g.ActiveId;
6156 else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow)
6157 window->DC.LastItemId = g.ActiveIdPreviousFrame;
6158 window->DC.LastItemRect = group_bb;
6159
6160 window->DC.GroupStack.pop_back();
6161
6162 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug]
6163 }
6164
6165 // Gets back to previous line and continue with horizontal layout
6166 // pos_x == 0 : follow right after previous item
6167 // pos_x != 0 : align to specified x position (relative to window/group left)
6168 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0
6169 // spacing_w >= 0 : enforce spacing amount
SameLine(float pos_x,float spacing_w)6170 void ImGui::SameLine(float pos_x, float spacing_w)
6171 {
6172 ImGuiWindow* window = GetCurrentWindow();
6173 if (window->SkipItems)
6174 return;
6175
6176 ImGuiContext& g = *GImGui;
6177 if (pos_x != 0.0f)
6178 {
6179 if (spacing_w < 0.0f) spacing_w = 0.0f;
6180 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
6181 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6182 }
6183 else
6184 {
6185 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x;
6186 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
6187 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
6188 }
6189 window->DC.CurrentLineSize = window->DC.PrevLineSize;
6190 window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
6191 }
6192
Indent(float indent_w)6193 void ImGui::Indent(float indent_w)
6194 {
6195 ImGuiContext& g = *GImGui;
6196 ImGuiWindow* window = GetCurrentWindow();
6197 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6198 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6199 }
6200
Unindent(float indent_w)6201 void ImGui::Unindent(float indent_w)
6202 {
6203 ImGuiContext& g = *GImGui;
6204 ImGuiWindow* window = GetCurrentWindow();
6205 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
6206 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
6207 }
6208
6209 //-----------------------------------------------------------------------------
6210 // [SECTION] TOOLTIPS
6211 //-----------------------------------------------------------------------------
6212
BeginTooltip()6213 void ImGui::BeginTooltip()
6214 {
6215 ImGuiContext& g = *GImGui;
6216 if (g.DragDropWithinSourceOrTarget)
6217 {
6218 // 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)
6219 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
6220 // 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.
6221 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
6222 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
6223 SetNextWindowPos(tooltip_pos);
6224 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
6225 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
6226 BeginTooltipEx(0, true);
6227 }
6228 else
6229 {
6230 BeginTooltipEx(0, false);
6231 }
6232 }
6233
6234 // 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)6235 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip)
6236 {
6237 ImGuiContext& g = *GImGui;
6238 char window_name[16];
6239 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
6240 if (override_previous_tooltip)
6241 if (ImGuiWindow* window = FindWindowByName(window_name))
6242 if (window->Active)
6243 {
6244 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
6245 window->Hidden = true;
6246 window->HiddenFramesRegular = 1;
6247 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
6248 }
6249 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav;
6250 Begin(window_name, NULL, flags | extra_flags);
6251 }
6252
EndTooltip()6253 void ImGui::EndTooltip()
6254 {
6255 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls
6256 End();
6257 }
6258
SetTooltipV(const char * fmt,va_list args)6259 void ImGui::SetTooltipV(const char* fmt, va_list args)
6260 {
6261 ImGuiContext& g = *GImGui;
6262 if (g.DragDropWithinSourceOrTarget)
6263 BeginTooltip();
6264 else
6265 BeginTooltipEx(0, true);
6266 TextV(fmt, args);
6267 EndTooltip();
6268 }
6269
SetTooltip(const char * fmt,...)6270 void ImGui::SetTooltip(const char* fmt, ...)
6271 {
6272 va_list args;
6273 va_start(args, fmt);
6274 SetTooltipV(fmt, args);
6275 va_end(args);
6276 }
6277
6278 //-----------------------------------------------------------------------------
6279 // [SECTION] POPUPS
6280 //-----------------------------------------------------------------------------
6281
IsPopupOpen(ImGuiID id)6282 bool ImGui::IsPopupOpen(ImGuiID id)
6283 {
6284 ImGuiContext& g = *GImGui;
6285 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
6286 }
6287
IsPopupOpen(const char * str_id)6288 bool ImGui::IsPopupOpen(const char* str_id)
6289 {
6290 ImGuiContext& g = *GImGui;
6291 return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
6292 }
6293
GetFrontMostPopupModal()6294 ImGuiWindow* ImGui::GetFrontMostPopupModal()
6295 {
6296 ImGuiContext& g = *GImGui;
6297 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--)
6298 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
6299 if (popup->Flags & ImGuiWindowFlags_Modal)
6300 return popup;
6301 return NULL;
6302 }
6303
OpenPopup(const char * str_id)6304 void ImGui::OpenPopup(const char* str_id)
6305 {
6306 ImGuiContext& g = *GImGui;
6307 OpenPopupEx(g.CurrentWindow->GetID(str_id));
6308 }
6309
6310 // Mark popup as open (toggle toward open state).
6311 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
6312 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
6313 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
OpenPopupEx(ImGuiID id)6314 void ImGui::OpenPopupEx(ImGuiID id)
6315 {
6316 ImGuiContext& g = *GImGui;
6317 ImGuiWindow* parent_window = g.CurrentWindow;
6318 int current_stack_size = g.CurrentPopupStack.Size;
6319 ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
6320 popup_ref.PopupId = id;
6321 popup_ref.Window = NULL;
6322 popup_ref.ParentWindow = parent_window;
6323 popup_ref.OpenFrameCount = g.FrameCount;
6324 popup_ref.OpenParentId = parent_window->IDStack.back();
6325 popup_ref.OpenMousePos = g.IO.MousePos;
6326 popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
6327
6328 //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id);
6329 if (g.OpenPopupStack.Size < current_stack_size + 1)
6330 {
6331 g.OpenPopupStack.push_back(popup_ref);
6332 }
6333 else
6334 {
6335 // 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
6336 // 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
6337 // 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.
6338 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)
6339 {
6340 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
6341 }
6342 else
6343 {
6344 // Close child popups if any, then flag popup for open/reopen
6345 g.OpenPopupStack.resize(current_stack_size + 1);
6346 g.OpenPopupStack[current_stack_size] = popup_ref;
6347 }
6348
6349 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
6350 // This is equivalent to what ClosePopupToLevel() does.
6351 //if (g.OpenPopupStack[current_stack_size].PopupId == id)
6352 // FocusWindow(parent_window);
6353 }
6354 }
6355
OpenPopupOnItemClick(const char * str_id,int mouse_button)6356 bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button)
6357 {
6358 ImGuiWindow* window = GImGui->CurrentWindow;
6359 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6360 {
6361 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!
6362 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6363 OpenPopupEx(id);
6364 return true;
6365 }
6366 return false;
6367 }
6368
ClosePopupsOverWindow(ImGuiWindow * ref_window)6369 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window)
6370 {
6371 ImGuiContext& g = *GImGui;
6372 if (g.OpenPopupStack.empty())
6373 return;
6374
6375 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
6376 // Don't close our own child popup windows.
6377 int n = 0;
6378 if (ref_window)
6379 {
6380 for (n = 0; n < g.OpenPopupStack.Size; n++)
6381 {
6382 ImGuiPopupRef& popup = g.OpenPopupStack[n];
6383 if (!popup.Window)
6384 continue;
6385 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
6386 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)
6387 continue;
6388
6389 // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow)
6390 bool has_focus = false;
6391 for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++)
6392 has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow);
6393 if (!has_focus)
6394 break;
6395 }
6396 }
6397 if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below
6398 ClosePopupToLevel(n);
6399 }
6400
ClosePopupToLevel(int remaining)6401 void ImGui::ClosePopupToLevel(int remaining)
6402 {
6403 IM_ASSERT(remaining >= 0);
6404 ImGuiContext& g = *GImGui;
6405 ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow;
6406 if (g.NavLayer == 0)
6407 focus_window = NavRestoreLastChildNavWindow(focus_window);
6408 FocusWindow(focus_window);
6409 focus_window->DC.NavHideHighlightOneFrame = true;
6410 g.OpenPopupStack.resize(remaining);
6411 }
6412
ClosePopup(ImGuiID id)6413 void ImGui::ClosePopup(ImGuiID id)
6414 {
6415 if (!IsPopupOpen(id))
6416 return;
6417 ImGuiContext& g = *GImGui;
6418 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
6419 }
6420
6421 // Close the popup we have begin-ed into.
CloseCurrentPopup()6422 void ImGui::CloseCurrentPopup()
6423 {
6424 ImGuiContext& g = *GImGui;
6425 int popup_idx = g.CurrentPopupStack.Size - 1;
6426 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
6427 return;
6428 while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu))
6429 popup_idx--;
6430 ClosePopupToLevel(popup_idx);
6431 }
6432
BeginPopupEx(ImGuiID id,ImGuiWindowFlags extra_flags)6433 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
6434 {
6435 ImGuiContext& g = *GImGui;
6436 if (!IsPopupOpen(id))
6437 {
6438 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6439 return false;
6440 }
6441
6442 char name[20];
6443 if (extra_flags & ImGuiWindowFlags_ChildMenu)
6444 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
6445 else
6446 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame
6447
6448 bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup);
6449 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
6450 EndPopup();
6451
6452 return is_open;
6453 }
6454
BeginPopup(const char * str_id,ImGuiWindowFlags flags)6455 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
6456 {
6457 ImGuiContext& g = *GImGui;
6458 if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
6459 {
6460 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6461 return false;
6462 }
6463 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6464 }
6465
BeginPopupModal(const char * name,bool * p_open,ImGuiWindowFlags flags)6466 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
6467 {
6468 ImGuiContext& g = *GImGui;
6469 ImGuiWindow* window = g.CurrentWindow;
6470 const ImGuiID id = window->GetID(name);
6471 if (!IsPopupOpen(id))
6472 {
6473 g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
6474 return false;
6475 }
6476
6477 // Center modal windows by default
6478 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
6479 if (g.NextWindowData.PosCond == 0)
6480 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
6481
6482 bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
6483 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
6484 {
6485 EndPopup();
6486 if (is_open)
6487 ClosePopup(id);
6488 return false;
6489 }
6490 return is_open;
6491 }
6492
EndPopup()6493 void ImGui::EndPopup()
6494 {
6495 ImGuiContext& g = *GImGui; (void)g;
6496 IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls
6497 IM_ASSERT(g.CurrentPopupStack.Size > 0);
6498
6499 // Make all menus and popups wrap around for now, may need to expose that policy.
6500 NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY);
6501
6502 End();
6503 }
6504
6505 // This is a helper to handle the simplest case of associating one named popup to one given widget.
6506 // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
6507 // You can pass a NULL str_id to use the identifier of the last item.
BeginPopupContextItem(const char * str_id,int mouse_button)6508 bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
6509 {
6510 ImGuiWindow* window = GImGui->CurrentWindow;
6511 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!
6512 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
6513 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6514 OpenPopupEx(id);
6515 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6516 }
6517
BeginPopupContextWindow(const char * str_id,int mouse_button,bool also_over_items)6518 bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
6519 {
6520 if (!str_id)
6521 str_id = "window_context";
6522 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6523 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
6524 if (also_over_items || !IsAnyItemHovered())
6525 OpenPopupEx(id);
6526 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6527 }
6528
BeginPopupContextVoid(const char * str_id,int mouse_button)6529 bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
6530 {
6531 if (!str_id)
6532 str_id = "void_context";
6533 ImGuiID id = GImGui->CurrentWindow->GetID(str_id);
6534 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
6535 OpenPopupEx(id);
6536 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
6537 }
6538
GetWindowAllowedExtentRect(ImGuiWindow *)6539 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*)
6540 {
6541 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding;
6542 ImRect r_screen = GetViewportRect();
6543 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
6544 return r_screen;
6545 }
6546
6547 // 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.)
6548 // 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)6549 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
6550 {
6551 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
6552 //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
6553 //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
6554
6555 // Combo Box policy (we want a connecting edge)
6556 if (policy == ImGuiPopupPositionPolicy_ComboBox)
6557 {
6558 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
6559 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6560 {
6561 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6562 if (n != -1 && dir == *last_dir) // Already tried this direction?
6563 continue;
6564 ImVec2 pos;
6565 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default)
6566 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
6567 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
6568 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
6569 if (!r_outer.Contains(ImRect(pos, pos + size)))
6570 continue;
6571 *last_dir = dir;
6572 return pos;
6573 }
6574 }
6575
6576 // Default popup policy
6577 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
6578 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
6579 {
6580 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
6581 if (n != -1 && dir == *last_dir) // Already tried this direction?
6582 continue;
6583 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);
6584 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);
6585 if (avail_w < size.x || avail_h < size.y)
6586 continue;
6587 ImVec2 pos;
6588 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
6589 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
6590 *last_dir = dir;
6591 return pos;
6592 }
6593
6594 // Fallback, try to keep within display
6595 *last_dir = ImGuiDir_None;
6596 ImVec2 pos = ref_pos;
6597 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
6598 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
6599 return pos;
6600 }
6601
FindBestWindowPosForPopup(ImGuiWindow * window)6602 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
6603 {
6604 ImGuiContext& g = *GImGui;
6605
6606 ImRect r_outer = GetWindowAllowedExtentRect(window);
6607 if (window->Flags & ImGuiWindowFlags_ChildMenu)
6608 {
6609 // 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.
6610 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
6611 IM_ASSERT(g.CurrentWindow == window);
6612 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2];
6613 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).
6614 ImRect r_avoid;
6615 if (parent_window->DC.MenuBarAppending)
6616 r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight());
6617 else
6618 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);
6619 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6620 }
6621 if (window->Flags & ImGuiWindowFlags_Popup)
6622 {
6623 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1);
6624 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6625 }
6626 if (window->Flags & ImGuiWindowFlags_Tooltip)
6627 {
6628 // Position tooltip (always follows mouse)
6629 float sc = g.Style.MouseCursorScale;
6630 ImVec2 ref_pos = NavCalcPreferredRefPos();
6631 ImRect r_avoid;
6632 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
6633 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
6634 else
6635 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.
6636 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid);
6637 if (window->AutoPosLastDirection == ImGuiDir_None)
6638 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.
6639 return pos;
6640 }
6641 IM_ASSERT(0);
6642 return window->Pos;
6643 }
6644
6645 //-----------------------------------------------------------------------------
6646 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION
6647 //-----------------------------------------------------------------------------
6648
ImGetDirQuadrantFromDelta(float dx,float dy)6649 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
6650 {
6651 if (ImFabs(dx) > ImFabs(dy))
6652 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
6653 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
6654 }
6655
NavScoreItemDistInterval(float a0,float a1,float b0,float b1)6656 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1)
6657 {
6658 if (a1 < b0)
6659 return a1 - b0;
6660 if (b1 < a0)
6661 return a0 - b1;
6662 return 0.0f;
6663 }
6664
NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir,ImRect & r,const ImRect & clip_rect)6665 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect)
6666 {
6667 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
6668 {
6669 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y);
6670 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y);
6671 }
6672 else
6673 {
6674 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x);
6675 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x);
6676 }
6677 }
6678
6679 // Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057
NavScoreItem(ImGuiNavMoveResult * result,ImRect cand)6680 static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand)
6681 {
6682 ImGuiContext& g = *GImGui;
6683 ImGuiWindow* window = g.CurrentWindow;
6684 if (g.NavLayer != window->DC.NavLayerCurrent)
6685 return false;
6686
6687 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)
6688 g.NavScoringCount++;
6689
6690 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
6691 if (window->ParentWindow == g.NavWindow)
6692 {
6693 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);
6694 if (!window->ClipRect.Contains(cand))
6695 return false;
6696 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
6697 }
6698
6699 // 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)
6700 // For example, this ensure that items in one column are not reached when moving vertically from items in another column.
6701 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect);
6702
6703 // Compute distance between boxes
6704 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
6705 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
6706 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
6707 if (dby != 0.0f && dbx != 0.0f)
6708 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
6709 float dist_box = ImFabs(dbx) + ImFabs(dby);
6710
6711 // 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)
6712 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
6713 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
6714 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
6715
6716 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
6717 ImGuiDir quadrant;
6718 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
6719 if (dbx != 0.0f || dby != 0.0f)
6720 {
6721 // For non-overlapping boxes, use distance between boxes
6722 dax = dbx;
6723 day = dby;
6724 dist_axial = dist_box;
6725 quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
6726 }
6727 else if (dcx != 0.0f || dcy != 0.0f)
6728 {
6729 // For overlapping boxes with different centers, use distance between centers
6730 dax = dcx;
6731 day = dcy;
6732 dist_axial = dist_center;
6733 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
6734 }
6735 else
6736 {
6737 // 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)
6738 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
6739 }
6740
6741 #if IMGUI_DEBUG_NAV_SCORING
6742 char buf[128];
6743 if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max))
6744 {
6745 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]);
6746 ImDrawList* draw_list = ImGui::GetOverlayDrawList();
6747 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100));
6748 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200));
6749 draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150));
6750 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf);
6751 }
6752 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate.
6753 {
6754 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; }
6755 if (quadrant == g.NavMoveDir)
6756 {
6757 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
6758 ImDrawList* draw_list = ImGui::GetOverlayDrawList();
6759 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200));
6760 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf);
6761 }
6762 }
6763 #endif
6764
6765 // Is it in the quadrant we're interesting in moving to?
6766 bool new_best = false;
6767 if (quadrant == g.NavMoveDir)
6768 {
6769 // Does it beat the current best candidate?
6770 if (dist_box < result->DistBox)
6771 {
6772 result->DistBox = dist_box;
6773 result->DistCenter = dist_center;
6774 return true;
6775 }
6776 if (dist_box == result->DistBox)
6777 {
6778 // Try using distance between center points to break ties
6779 if (dist_center < result->DistCenter)
6780 {
6781 result->DistCenter = dist_center;
6782 new_best = true;
6783 }
6784 else if (dist_center == result->DistCenter)
6785 {
6786 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
6787 // (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),
6788 // 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.
6789 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
6790 new_best = true;
6791 }
6792 }
6793 }
6794
6795 // 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
6796 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
6797 // 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.
6798 // 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.
6799 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
6800 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match
6801 if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
6802 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))
6803 {
6804 result->DistAxial = dist_axial;
6805 new_best = true;
6806 }
6807
6808 return new_best;
6809 }
6810
6811 // 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)6812 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id)
6813 {
6814 ImGuiContext& g = *GImGui;
6815 //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.
6816 // return;
6817
6818 const ImGuiItemFlags item_flags = window->DC.ItemFlags;
6819 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos);
6820
6821 // Process Init Request
6822 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent)
6823 {
6824 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
6825 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0)
6826 {
6827 g.NavInitResultId = id;
6828 g.NavInitResultRectRel = nav_bb_rel;
6829 }
6830 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus))
6831 {
6832 g.NavInitRequest = false; // Found a match, clear request
6833 NavUpdateAnyRequestFlag();
6834 }
6835 }
6836
6837 // Process Move Request (scoring for navigation)
6838 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy)
6839 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav))
6840 {
6841 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
6842 #if IMGUI_DEBUG_NAV_SCORING
6843 // [DEBUG] Score all items in NavWindow at all times
6844 if (!g.NavMoveRequest)
6845 g.NavMoveDir = g.NavMoveDirLast;
6846 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest;
6847 #else
6848 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb);
6849 #endif
6850 if (new_best)
6851 {
6852 result->ID = id;
6853 result->Window = window;
6854 result->RectRel = nav_bb_rel;
6855 }
6856
6857 const float VISIBLE_RATIO = 0.70f;
6858 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
6859 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)
6860 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb))
6861 {
6862 result = &g.NavMoveResultLocalVisibleSet;
6863 result->ID = id;
6864 result->Window = window;
6865 result->RectRel = nav_bb_rel;
6866 }
6867 }
6868
6869 // Update window-relative bounding box of navigated item
6870 if (g.NavId == id)
6871 {
6872 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window.
6873 g.NavLayer = window->DC.NavLayerCurrent;
6874 g.NavIdIsAlive = true;
6875 g.NavIdTabCounter = window->FocusIdxTabCounter;
6876 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position)
6877 }
6878 }
6879
NavMoveRequestButNoResultYet()6880 bool ImGui::NavMoveRequestButNoResultYet()
6881 {
6882 ImGuiContext& g = *GImGui;
6883 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
6884 }
6885
NavMoveRequestCancel()6886 void ImGui::NavMoveRequestCancel()
6887 {
6888 ImGuiContext& g = *GImGui;
6889 g.NavMoveRequest = false;
6890 NavUpdateAnyRequestFlag();
6891 }
6892
NavMoveRequestForward(ImGuiDir move_dir,ImGuiDir clip_dir,const ImRect & bb_rel,ImGuiNavMoveFlags move_flags)6893 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags)
6894 {
6895 ImGuiContext& g = *GImGui;
6896 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None);
6897 ImGui::NavMoveRequestCancel();
6898 g.NavMoveDir = move_dir;
6899 g.NavMoveClipDir = clip_dir;
6900 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued;
6901 g.NavMoveRequestFlags = move_flags;
6902 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel;
6903 }
6904
NavMoveRequestTryWrapping(ImGuiWindow * window,ImGuiNavMoveFlags move_flags)6905 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags)
6906 {
6907 ImGuiContext& g = *GImGui;
6908 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0)
6909 return;
6910 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping
6911 ImRect bb_rel = window->NavRectRel[0];
6912
6913 ImGuiDir clip_dir = g.NavMoveDir;
6914 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
6915 {
6916 bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x;
6917 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; }
6918 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6919 }
6920 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
6921 {
6922 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x;
6923 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; }
6924 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6925 }
6926 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
6927 {
6928 bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y;
6929 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; }
6930 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6931 }
6932 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
6933 {
6934 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y;
6935 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; }
6936 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags);
6937 }
6938 }
6939
NavSaveLastChildNavWindow(ImGuiWindow * nav_window)6940 static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window)
6941 {
6942 ImGuiWindow* parent_window = nav_window;
6943 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
6944 parent_window = parent_window->ParentWindow;
6945 if (parent_window && parent_window != nav_window)
6946 parent_window->NavLastChildNavWindow = nav_window;
6947 }
6948
6949 // Call when we are expected to land on Layer 0 after FocusWindow()
NavRestoreLastChildNavWindow(ImGuiWindow * window)6950 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
6951 {
6952 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window;
6953 }
6954
NavRestoreLayer(int layer)6955 static void NavRestoreLayer(int layer)
6956 {
6957 ImGuiContext& g = *GImGui;
6958 g.NavLayer = layer;
6959 if (layer == 0)
6960 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
6961 if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
6962 ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
6963 else
6964 ImGui::NavInitWindow(g.NavWindow, true);
6965 }
6966
NavUpdateAnyRequestFlag()6967 static inline void ImGui::NavUpdateAnyRequestFlag()
6968 {
6969 ImGuiContext& g = *GImGui;
6970 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
6971 if (g.NavAnyRequest)
6972 IM_ASSERT(g.NavWindow != NULL);
6973 }
6974
6975 // This needs to be called before we submit any widget (aka in or before Begin)
NavInitWindow(ImGuiWindow * window,bool force_reinit)6976 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
6977 {
6978 ImGuiContext& g = *GImGui;
6979 IM_ASSERT(window == g.NavWindow);
6980 bool init_for_nav = false;
6981 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs))
6982 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
6983 init_for_nav = true;
6984 if (init_for_nav)
6985 {
6986 SetNavID(0, g.NavLayer);
6987 g.NavInitRequest = true;
6988 g.NavInitRequestFromMove = false;
6989 g.NavInitResultId = 0;
6990 g.NavInitResultRectRel = ImRect();
6991 NavUpdateAnyRequestFlag();
6992 }
6993 else
6994 {
6995 g.NavId = window->NavLastIds[0];
6996 }
6997 }
6998
NavCalcPreferredRefPos()6999 static ImVec2 ImGui::NavCalcPreferredRefPos()
7000 {
7001 ImGuiContext& g = *GImGui;
7002 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
7003 return ImFloor(g.IO.MousePos);
7004
7005 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item
7006 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
7007 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()));
7008 ImRect visible_rect = GetViewportRect();
7009 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.
7010 }
7011
GetNavInputAmount(ImGuiNavInput n,ImGuiInputReadMode mode)7012 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode)
7013 {
7014 ImGuiContext& g = *GImGui;
7015 if (mode == ImGuiInputReadMode_Down)
7016 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user)
7017
7018 const float t = g.IO.NavInputsDownDuration[n];
7019 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input.
7020 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f);
7021 if (t < 0.0f)
7022 return 0.0f;
7023 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input.
7024 return (t == 0.0f) ? 1.0f : 0.0f;
7025 if (mode == ImGuiInputReadMode_Repeat)
7026 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f);
7027 if (mode == ImGuiInputReadMode_RepeatSlow)
7028 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f);
7029 if (mode == ImGuiInputReadMode_RepeatFast)
7030 return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f);
7031 return 0.0f;
7032 }
7033
GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources,ImGuiInputReadMode mode,float slow_factor,float fast_factor)7034 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor)
7035 {
7036 ImVec2 delta(0.0f, 0.0f);
7037 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
7038 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode));
7039 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
7040 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode));
7041 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick)
7042 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode));
7043 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow))
7044 delta *= slow_factor;
7045 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast))
7046 delta *= fast_factor;
7047 return delta;
7048 }
7049
7050 // Scroll to keep newly navigated item fully into view
7051 // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated.
NavScrollToBringItemIntoView(ImGuiWindow * window,const ImRect & item_rect)7052 static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect)
7053 {
7054 ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1));
7055 //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG]
7056 if (window_rect.Contains(item_rect))
7057 return;
7058
7059 ImGuiContext& g = *GImGui;
7060 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x)
7061 {
7062 window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x;
7063 window->ScrollTargetCenterRatio.x = 0.0f;
7064 }
7065 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x)
7066 {
7067 window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x;
7068 window->ScrollTargetCenterRatio.x = 1.0f;
7069 }
7070 if (item_rect.Min.y < window_rect.Min.y)
7071 {
7072 window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y;
7073 window->ScrollTargetCenterRatio.y = 0.0f;
7074 }
7075 else if (item_rect.Max.y >= window_rect.Max.y)
7076 {
7077 window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y;
7078 window->ScrollTargetCenterRatio.y = 1.0f;
7079 }
7080 }
7081
NavUpdate()7082 static void ImGui::NavUpdate()
7083 {
7084 ImGuiContext& g = *GImGui;
7085 g.IO.WantSetMousePos = false;
7086 #if 0
7087 if (g.NavScoringCount > 0) printf("[%05d] 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);
7088 #endif
7089
7090 // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard)
7091 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
7092 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
7093 if (nav_gamepad_active)
7094 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)
7095 g.NavInputSource = ImGuiInputSource_NavGamepad;
7096
7097 // Update Keyboard->Nav inputs mapping
7098 if (nav_keyboard_active)
7099 {
7100 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; }
7101 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate );
7102 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input );
7103 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel );
7104 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ );
7105 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_);
7106 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ );
7107 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ );
7108 if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f;
7109 if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f;
7110 if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f;
7111 #undef NAV_MAP_KEY
7112 }
7113 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration));
7114 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++)
7115 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;
7116
7117 // Process navigation init request (select first/default focus)
7118 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove))
7119 {
7120 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
7121 IM_ASSERT(g.NavWindow);
7122 if (g.NavInitRequestFromMove)
7123 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel);
7124 else
7125 SetNavID(g.NavInitResultId, g.NavLayer);
7126 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel;
7127 }
7128 g.NavInitRequest = false;
7129 g.NavInitRequestFromMove = false;
7130 g.NavInitResultId = 0;
7131 g.NavJustMovedToId = 0;
7132
7133 // Process navigation move request
7134 if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0))
7135 NavUpdateMoveResult();
7136
7137 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame
7138 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive)
7139 {
7140 IM_ASSERT(g.NavMoveRequest);
7141 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0)
7142 g.NavDisableHighlight = false;
7143 g.NavMoveRequestForward = ImGuiNavForward_None;
7144 }
7145
7146 // Apply application mouse position movement, after we had a chance to process move request result.
7147 if (g.NavMousePosDirty && g.NavIdIsAlive)
7148 {
7149 // Set mouse position given our knowledge of the navigated item position from last frame
7150 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
7151 {
7152 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)
7153 {
7154 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos();
7155 g.IO.WantSetMousePos = true;
7156 }
7157 }
7158 g.NavMousePosDirty = false;
7159 }
7160 g.NavIdIsAlive = false;
7161 g.NavJustTabbedId = 0;
7162 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1);
7163
7164 // 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
7165 if (g.NavWindow)
7166 NavSaveLastChildNavWindow(g.NavWindow);
7167 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0)
7168 g.NavWindow->NavLastChildNavWindow = NULL;
7169
7170 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
7171 NavUpdateWindowing();
7172
7173 // Set output flags for user application
7174 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
7175 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest;
7176
7177 // Process NavCancel input (to close a popup, get back to parent, clear focus)
7178 if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed))
7179 {
7180 if (g.ActiveId != 0)
7181 {
7182 ClearActiveID();
7183 }
7184 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
7185 {
7186 // Exit child window
7187 ImGuiWindow* child_window = g.NavWindow;
7188 ImGuiWindow* parent_window = g.NavWindow->ParentWindow;
7189 IM_ASSERT(child_window->ChildId != 0);
7190 FocusWindow(parent_window);
7191 SetNavID(child_window->ChildId, 0);
7192 g.NavIdIsAlive = false;
7193 if (g.NavDisableMouseHover)
7194 g.NavMousePosDirty = true;
7195 }
7196 else if (g.OpenPopupStack.Size > 0)
7197 {
7198 // Close open popup/menu
7199 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
7200 ClosePopupToLevel(g.OpenPopupStack.Size - 1);
7201 }
7202 else if (g.NavLayer != 0)
7203 {
7204 // Leave the "menu" layer
7205 NavRestoreLayer(0);
7206 }
7207 else
7208 {
7209 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
7210 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
7211 g.NavWindow->NavLastIds[0] = 0;
7212 g.NavId = 0;
7213 }
7214 }
7215
7216 // Process manual activation request
7217 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0;
7218 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7219 {
7220 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate);
7221 bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed);
7222 if (g.ActiveId == 0 && activate_pressed)
7223 g.NavActivateId = g.NavId;
7224 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down)
7225 g.NavActivateDownId = g.NavId;
7226 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed)
7227 g.NavActivatePressedId = g.NavId;
7228 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed))
7229 g.NavInputId = g.NavId;
7230 }
7231 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7232 g.NavDisableHighlight = true;
7233 if (g.NavActivateId != 0)
7234 IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
7235 g.NavMoveRequest = false;
7236
7237 // Process programmatic activation request
7238 if (g.NavNextActivateId != 0)
7239 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId;
7240 g.NavNextActivateId = 0;
7241
7242 // Initiate directional inputs request
7243 const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags;
7244 if (g.NavMoveRequestForward == ImGuiNavForward_None)
7245 {
7246 g.NavMoveDir = ImGuiDir_None;
7247 g.NavMoveRequestFlags = 0;
7248 if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
7249 {
7250 if ((allowed_dir_flags & (1<<ImGuiDir_Left)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadLeft, ImGuiNavInput_KeyLeft_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Left;
7251 if ((allowed_dir_flags & (1<<ImGuiDir_Right)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadRight,ImGuiNavInput_KeyRight_,ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Right;
7252 if ((allowed_dir_flags & (1<<ImGuiDir_Up)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadUp, ImGuiNavInput_KeyUp_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Up;
7253 if ((allowed_dir_flags & (1<<ImGuiDir_Down)) && IsNavInputPressedAnyOfTwo(ImGuiNavInput_DpadDown, ImGuiNavInput_KeyDown_, ImGuiInputReadMode_Repeat)) g.NavMoveDir = ImGuiDir_Down;
7254 }
7255 g.NavMoveClipDir = g.NavMoveDir;
7256 }
7257 else
7258 {
7259 // 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)
7260 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function)
7261 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
7262 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued);
7263 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive;
7264 }
7265
7266 // Update PageUp/PageDown scroll
7267 float nav_scoring_rect_offset_y = 0.0f;
7268 if (nav_keyboard_active)
7269 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags);
7270
7271 // 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
7272 if (g.NavMoveDir != ImGuiDir_None)
7273 {
7274 g.NavMoveRequest = true;
7275 g.NavMoveDirLast = g.NavMoveDir;
7276 }
7277 if (g.NavMoveRequest && g.NavId == 0)
7278 {
7279 g.NavInitRequest = g.NavInitRequestFromMove = true;
7280 g.NavInitResultId = 0;
7281 g.NavDisableHighlight = false;
7282 }
7283 NavUpdateAnyRequestFlag();
7284
7285 // Scrolling
7286 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
7287 {
7288 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
7289 ImGuiWindow* window = g.NavWindow;
7290 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.
7291 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
7292 {
7293 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
7294 SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
7295 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
7296 SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
7297 }
7298
7299 // *Normal* Manual scroll with NavScrollXXX keys
7300 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
7301 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
7302 if (scroll_dir.x != 0.0f && window->ScrollbarX)
7303 {
7304 SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
7305 g.NavMoveFromClampedRefRect = true;
7306 }
7307 if (scroll_dir.y != 0.0f)
7308 {
7309 SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
7310 g.NavMoveFromClampedRefRect = true;
7311 }
7312 }
7313
7314 // Reset search results
7315 g.NavMoveResultLocal.Clear();
7316 g.NavMoveResultLocalVisibleSet.Clear();
7317 g.NavMoveResultOther.Clear();
7318
7319 // 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
7320 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0)
7321 {
7322 ImGuiWindow* window = g.NavWindow;
7323 ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1));
7324 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
7325 {
7326 float pad = window->CalcFontSize() * 0.5f;
7327 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
7328 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel);
7329 g.NavId = 0;
7330 }
7331 g.NavMoveFromClampedRefRect = false;
7332 }
7333
7334 // 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)
7335 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0);
7336 g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect();
7337 g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y);
7338 g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x);
7339 g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x;
7340 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().
7341 //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG]
7342 g.NavScoringCount = 0;
7343 #if IMGUI_DEBUG_NAV_RECTS
7344 if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList()->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG]
7345 if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
7346 #endif
7347 }
7348
NavUpdateMoveResult()7349 static void ImGui::NavUpdateMoveResult()
7350 {
7351 // Select which result to use
7352 ImGuiContext& g = *GImGui;
7353 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
7354
7355 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
7356 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
7357 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId)
7358 result = &g.NavMoveResultLocalVisibleSet;
7359
7360 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
7361 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
7362 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
7363 result = &g.NavMoveResultOther;
7364 IM_ASSERT(g.NavWindow && result->Window);
7365
7366 // Scroll to keep newly navigated item fully into view.
7367 if (g.NavLayer == 0)
7368 {
7369 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos);
7370 NavScrollToBringItemIntoView(result->Window, rect_abs);
7371
7372 // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate()
7373 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false);
7374 ImVec2 delta_scroll = result->Window->Scroll - next_scroll;
7375 result->RectRel.Translate(delta_scroll);
7376
7377 // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy).
7378 if (result->Window->Flags & ImGuiWindowFlags_ChildWindow)
7379 NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll));
7380 }
7381
7382 // Apply result from previous frame navigation directional move request
7383 ClearActiveID();
7384 g.NavWindow = result->Window;
7385 SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel);
7386 g.NavJustMovedToId = result->ID;
7387 g.NavMoveFromClampedRefRect = false;
7388 }
7389
NavUpdatePageUpPageDown(int allowed_dir_flags)7390 static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
7391 {
7392 ImGuiContext& g = *GImGui;
7393 if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0)
7394 {
7395 ImGuiWindow* window = g.NavWindow;
7396 bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up));
7397 bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down));
7398 if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held))
7399 {
7400 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll)
7401 {
7402 // Fallback manual-scroll when window has no navigable item
7403 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7404 SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight());
7405 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7406 SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight());
7407 }
7408 else
7409 {
7410 const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
7411 const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
7412 float nav_scoring_rect_offset_y = 0.0f;
7413 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
7414 {
7415 nav_scoring_rect_offset_y = -page_offset_y;
7416 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)
7417 g.NavMoveClipDir = ImGuiDir_Up;
7418 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7419 }
7420 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
7421 {
7422 nav_scoring_rect_offset_y = +page_offset_y;
7423 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)
7424 g.NavMoveClipDir = ImGuiDir_Down;
7425 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
7426 }
7427 return nav_scoring_rect_offset_y;
7428 }
7429 }
7430 }
7431 return 0.0f;
7432 }
7433
FindWindowIndex(ImGuiWindow * window)7434 static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N)
7435 {
7436 ImGuiContext& g = *GImGui;
7437 for (int i = g.Windows.Size-1; i >= 0; i--)
7438 if (g.Windows[i] == window)
7439 return i;
7440 return -1;
7441 }
7442
FindWindowNavFocusable(int i_start,int i_stop,int dir)7443 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
7444 {
7445 ImGuiContext& g = *GImGui;
7446 for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir)
7447 if (ImGui::IsWindowNavFocusable(g.Windows[i]))
7448 return g.Windows[i];
7449 return NULL;
7450 }
7451
NavUpdateWindowingHighlightWindow(int focus_change_dir)7452 static void NavUpdateWindowingHighlightWindow(int focus_change_dir)
7453 {
7454 ImGuiContext& g = *GImGui;
7455 IM_ASSERT(g.NavWindowingTarget);
7456 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
7457 return;
7458
7459 const int i_current = FindWindowIndex(g.NavWindowingTarget);
7460 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
7461 if (!window_target)
7462 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir);
7463 if (window_target) // Don't reset windowing target if there's a single window in the list
7464 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
7465 g.NavWindowingToggleLayer = false;
7466 }
7467
7468 // Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer)
NavUpdateWindowing()7469 static void ImGui::NavUpdateWindowing()
7470 {
7471 ImGuiContext& g = *GImGui;
7472 ImGuiWindow* apply_focus_window = NULL;
7473 bool apply_toggle_layer = false;
7474
7475 ImGuiWindow* modal_window = GetFrontMostPopupModal();
7476 if (modal_window != NULL)
7477 {
7478 g.NavWindowingTarget = NULL;
7479 return;
7480 }
7481
7482 // Fade out
7483 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
7484 {
7485 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f);
7486 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
7487 g.NavWindowingTargetAnim = NULL;
7488 }
7489
7490 // Start CTRL-TAB or Square+L/R window selection
7491 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
7492 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
7493 if (start_windowing_with_gamepad || start_windowing_with_keyboard)
7494 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1))
7495 {
7496 g.NavWindowingTarget = g.NavWindowingTargetAnim = window;
7497 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
7498 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true;
7499 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad;
7500 }
7501
7502 // Gamepad update
7503 g.NavWindowingTimer += g.IO.DeltaTime;
7504 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad)
7505 {
7506 // 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
7507 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
7508
7509 // Select window to focus
7510 const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow);
7511 if (focus_change_dir != 0)
7512 {
7513 NavUpdateWindowingHighlightWindow(focus_change_dir);
7514 g.NavWindowingHighlightAlpha = 1.0f;
7515 }
7516
7517 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most)
7518 if (!IsNavInputDown(ImGuiNavInput_Menu))
7519 {
7520 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
7521 if (g.NavWindowingToggleLayer && g.NavWindow)
7522 apply_toggle_layer = true;
7523 else if (!g.NavWindowingToggleLayer)
7524 apply_focus_window = g.NavWindowingTarget;
7525 g.NavWindowingTarget = NULL;
7526 }
7527 }
7528
7529 // Keyboard: Focus
7530 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard)
7531 {
7532 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
7533 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
7534 if (IsKeyPressedMap(ImGuiKey_Tab, true))
7535 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1);
7536 if (!g.IO.KeyCtrl)
7537 apply_focus_window = g.NavWindowingTarget;
7538 }
7539
7540 // Keyboard: Press and Release ALT to toggle menu layer
7541 // 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
7542 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released))
7543 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev))
7544 apply_toggle_layer = true;
7545
7546 // Move window
7547 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
7548 {
7549 ImVec2 move_delta;
7550 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift)
7551 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down);
7552 if (g.NavInputSource == ImGuiInputSource_NavGamepad)
7553 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down);
7554 if (move_delta.x != 0.0f || move_delta.y != 0.0f)
7555 {
7556 const float NAV_MOVE_SPEED = 800.0f;
7557 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
7558 g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed;
7559 g.NavDisableMouseHover = true;
7560 MarkIniSettingsDirty(g.NavWindowingTarget);
7561 }
7562 }
7563
7564 // Apply final focus
7565 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
7566 {
7567 g.NavDisableHighlight = false;
7568 g.NavDisableMouseHover = true;
7569 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window);
7570 ClosePopupsOverWindow(apply_focus_window);
7571 FocusWindow(apply_focus_window);
7572 if (apply_focus_window->NavLastIds[0] == 0)
7573 NavInitWindow(apply_focus_window, false);
7574
7575 // If the window only has a menu layer, select it directly
7576 if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1))
7577 g.NavLayer = 1;
7578 }
7579 if (apply_focus_window)
7580 g.NavWindowingTarget = NULL;
7581
7582 // Apply menu/layer toggle
7583 if (apply_toggle_layer && g.NavWindow)
7584 {
7585 // Move to parent menu if necessary
7586 ImGuiWindow* new_nav_window = g.NavWindow;
7587 while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
7588 new_nav_window = new_nav_window->ParentWindow;
7589 if (new_nav_window != g.NavWindow)
7590 {
7591 ImGuiWindow* old_nav_window = g.NavWindow;
7592 FocusWindow(new_nav_window);
7593 new_nav_window->NavLastChildNavWindow = old_nav_window;
7594 }
7595 g.NavDisableHighlight = false;
7596 g.NavDisableMouseHover = true;
7597 NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
7598 }
7599 }
7600
7601 // Window has already passed the IsWindowNavFocusable()
GetFallbackWindowNameForWindowingList(ImGuiWindow * window)7602 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
7603 {
7604 if (window->Flags & ImGuiWindowFlags_Popup)
7605 return "(Popup)";
7606 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
7607 return "(Main menu bar)";
7608 return "(Untitled)";
7609 }
7610
7611 // Overlay displayed when using CTRL+TAB. Called by EndFrame().
NavUpdateWindowingList()7612 void ImGui::NavUpdateWindowingList()
7613 {
7614 ImGuiContext& g = *GImGui;
7615 IM_ASSERT(g.NavWindowingTarget != NULL);
7616
7617 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
7618 return;
7619
7620 if (g.NavWindowingList == NULL)
7621 g.NavWindowingList = FindWindowByName("###NavWindowingList");
7622 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
7623 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
7624 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
7625 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
7626 for (int n = g.Windows.Size - 1; n >= 0; n--)
7627 {
7628 ImGuiWindow* window = g.Windows[n];
7629 if (!IsWindowNavFocusable(window))
7630 continue;
7631 const char* label = window->Name;
7632 if (label == FindRenderedTextEnd(label))
7633 label = GetFallbackWindowNameForWindowingList(window);
7634 Selectable(label, g.NavWindowingTarget == window);
7635 }
7636 End();
7637 PopStyleVar();
7638 }
7639
7640 //-----------------------------------------------------------------------------
7641 // [SECTION] COLUMNS
7642 // In the current version, Columns are very weak. Needs to be replaced with a more full-featured system.
7643 //-----------------------------------------------------------------------------
7644
NextColumn()7645 void ImGui::NextColumn()
7646 {
7647 ImGuiWindow* window = GetCurrentWindow();
7648 if (window->SkipItems || window->DC.ColumnsSet == NULL)
7649 return;
7650
7651 ImGuiContext& g = *GImGui;
7652 PopItemWidth();
7653 PopClipRect();
7654
7655 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7656 columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
7657 if (++columns->Current < columns->Count)
7658 {
7659 // Columns 1+ cancel out IndentX
7660 window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + g.Style.ItemSpacing.x;
7661 window->DrawList->ChannelsSetCurrent(columns->Current);
7662 }
7663 else
7664 {
7665 window->DC.ColumnsOffset.x = 0.0f;
7666 window->DrawList->ChannelsSetCurrent(0);
7667 columns->Current = 0;
7668 columns->LineMinY = columns->LineMaxY;
7669 }
7670 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7671 window->DC.CursorPos.y = columns->LineMinY;
7672 window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f);
7673 window->DC.CurrentLineTextBaseOffset = 0.0f;
7674
7675 PushColumnClipRect();
7676 PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup
7677 }
7678
GetColumnIndex()7679 int ImGui::GetColumnIndex()
7680 {
7681 ImGuiWindow* window = GetCurrentWindowRead();
7682 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0;
7683 }
7684
GetColumnsCount()7685 int ImGui::GetColumnsCount()
7686 {
7687 ImGuiWindow* window = GetCurrentWindowRead();
7688 return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1;
7689 }
7690
OffsetNormToPixels(const ImGuiColumnsSet * columns,float offset_norm)7691 static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm)
7692 {
7693 return offset_norm * (columns->MaxX - columns->MinX);
7694 }
7695
PixelsToOffsetNorm(const ImGuiColumnsSet * columns,float offset)7696 static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset)
7697 {
7698 return offset / (columns->MaxX - columns->MinX);
7699 }
7700
GetColumnsRectHalfWidth()7701 static inline float GetColumnsRectHalfWidth() { return 4.0f; }
7702
GetDraggedColumnOffset(ImGuiColumnsSet * columns,int column_index)7703 static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index)
7704 {
7705 // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
7706 // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
7707 ImGuiContext& g = *GImGui;
7708 ImGuiWindow* window = g.CurrentWindow;
7709 IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
7710 IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
7711
7712 float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x;
7713 x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
7714 if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths))
7715 x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
7716
7717 return x;
7718 }
7719
GetColumnOffset(int column_index)7720 float ImGui::GetColumnOffset(int column_index)
7721 {
7722 ImGuiWindow* window = GetCurrentWindowRead();
7723 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7724 IM_ASSERT(columns != NULL);
7725
7726 if (column_index < 0)
7727 column_index = columns->Current;
7728 IM_ASSERT(column_index < columns->Columns.Size);
7729
7730 const float t = columns->Columns[column_index].OffsetNorm;
7731 const float x_offset = ImLerp(columns->MinX, columns->MaxX, t);
7732 return x_offset;
7733 }
7734
GetColumnWidthEx(ImGuiColumnsSet * columns,int column_index,bool before_resize=false)7735 static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false)
7736 {
7737 if (column_index < 0)
7738 column_index = columns->Current;
7739
7740 float offset_norm;
7741 if (before_resize)
7742 offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
7743 else
7744 offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
7745 return OffsetNormToPixels(columns, offset_norm);
7746 }
7747
GetColumnWidth(int column_index)7748 float ImGui::GetColumnWidth(int column_index)
7749 {
7750 ImGuiWindow* window = GetCurrentWindowRead();
7751 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7752 IM_ASSERT(columns != NULL);
7753
7754 if (column_index < 0)
7755 column_index = columns->Current;
7756 return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
7757 }
7758
SetColumnOffset(int column_index,float offset)7759 void ImGui::SetColumnOffset(int column_index, float offset)
7760 {
7761 ImGuiContext& g = *GImGui;
7762 ImGuiWindow* window = g.CurrentWindow;
7763 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7764 IM_ASSERT(columns != NULL);
7765
7766 if (column_index < 0)
7767 column_index = columns->Current;
7768 IM_ASSERT(column_index < columns->Columns.Size);
7769
7770 const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1);
7771 const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
7772
7773 if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow))
7774 offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
7775 columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX);
7776
7777 if (preserve_width)
7778 SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
7779 }
7780
SetColumnWidth(int column_index,float width)7781 void ImGui::SetColumnWidth(int column_index, float width)
7782 {
7783 ImGuiWindow* window = GetCurrentWindowRead();
7784 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7785 IM_ASSERT(columns != NULL);
7786
7787 if (column_index < 0)
7788 column_index = columns->Current;
7789 SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
7790 }
7791
PushColumnClipRect(int column_index)7792 void ImGui::PushColumnClipRect(int column_index)
7793 {
7794 ImGuiWindow* window = GetCurrentWindowRead();
7795 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7796 if (column_index < 0)
7797 column_index = columns->Current;
7798
7799 PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false);
7800 }
7801
FindOrAddColumnsSet(ImGuiWindow * window,ImGuiID id)7802 static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id)
7803 {
7804 for (int n = 0; n < window->ColumnsStorage.Size; n++)
7805 if (window->ColumnsStorage[n].ID == id)
7806 return &window->ColumnsStorage[n];
7807
7808 window->ColumnsStorage.push_back(ImGuiColumnsSet());
7809 ImGuiColumnsSet* columns = &window->ColumnsStorage.back();
7810 columns->ID = id;
7811 return columns;
7812 }
7813
BeginColumns(const char * str_id,int columns_count,ImGuiColumnsFlags flags)7814 void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags)
7815 {
7816 ImGuiContext& g = *GImGui;
7817 ImGuiWindow* window = GetCurrentWindow();
7818
7819 IM_ASSERT(columns_count > 1);
7820 IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported
7821
7822 // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
7823 // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
7824 PushID(0x11223347 + (str_id ? 0 : columns_count));
7825 ImGuiID id = window->GetID(str_id ? str_id : "columns");
7826 PopID();
7827
7828 // Acquire storage for the columns set
7829 ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id);
7830 IM_ASSERT(columns->ID == id);
7831 columns->Current = 0;
7832 columns->Count = columns_count;
7833 columns->Flags = flags;
7834 window->DC.ColumnsSet = columns;
7835
7836 // Set state for first column
7837 const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x);
7838 columns->MinX = window->DC.Indent.x - g.Style.ItemSpacing.x; // Lock our horizontal range
7839 columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f);
7840 columns->StartPosY = window->DC.CursorPos.y;
7841 columns->StartMaxPosX = window->DC.CursorMaxPos.x;
7842 columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
7843 window->DC.ColumnsOffset.x = 0.0f;
7844 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7845
7846 // Clear data if columns count changed
7847 if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
7848 columns->Columns.resize(0);
7849
7850 // Initialize defaults
7851 columns->IsFirstFrame = (columns->Columns.Size == 0);
7852 if (columns->Columns.Size == 0)
7853 {
7854 columns->Columns.reserve(columns_count + 1);
7855 for (int n = 0; n < columns_count + 1; n++)
7856 {
7857 ImGuiColumnData column;
7858 column.OffsetNorm = n / (float)columns_count;
7859 columns->Columns.push_back(column);
7860 }
7861 }
7862
7863 for (int n = 0; n < columns_count; n++)
7864 {
7865 // Compute clipping rectangle
7866 ImGuiColumnData* column = &columns->Columns[n];
7867 float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f);
7868 float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
7869 column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
7870 column->ClipRect.ClipWith(window->ClipRect);
7871 }
7872
7873 window->DrawList->ChannelsSplit(columns->Count);
7874 PushColumnClipRect();
7875 PushItemWidth(GetColumnWidth() * 0.65f);
7876 }
7877
EndColumns()7878 void ImGui::EndColumns()
7879 {
7880 ImGuiContext& g = *GImGui;
7881 ImGuiWindow* window = GetCurrentWindow();
7882 ImGuiColumnsSet* columns = window->DC.ColumnsSet;
7883 IM_ASSERT(columns != NULL);
7884
7885 PopItemWidth();
7886 PopClipRect();
7887 window->DrawList->ChannelsMerge();
7888
7889 columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
7890 window->DC.CursorPos.y = columns->LineMaxY;
7891 if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize))
7892 window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent
7893
7894 // Draw columns borders and handle resize
7895 bool is_being_resized = false;
7896 if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems)
7897 {
7898 const float y1 = columns->StartPosY;
7899 const float y2 = window->DC.CursorPos.y;
7900 int dragging_column = -1;
7901 for (int n = 1; n < columns->Count; n++)
7902 {
7903 float x = window->Pos.x + GetColumnOffset(n);
7904 const ImGuiID column_id = columns->ID + ImGuiID(n);
7905 const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction
7906 const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2));
7907 KeepAliveID(column_id);
7908 if (IsClippedEx(column_rect, column_id, false))
7909 continue;
7910
7911 bool hovered = false, held = false;
7912 if (!(columns->Flags & ImGuiColumnsFlags_NoResize))
7913 {
7914 ButtonBehavior(column_rect, column_id, &hovered, &held);
7915 if (hovered || held)
7916 g.MouseCursor = ImGuiMouseCursor_ResizeEW;
7917 if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize))
7918 dragging_column = n;
7919 }
7920
7921 // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.)
7922 const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
7923 const float xi = (float)(int)x;
7924 window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col);
7925 }
7926
7927 // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
7928 if (dragging_column != -1)
7929 {
7930 if (!columns->IsBeingResized)
7931 for (int n = 0; n < columns->Count + 1; n++)
7932 columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
7933 columns->IsBeingResized = is_being_resized = true;
7934 float x = GetDraggedColumnOffset(columns, dragging_column);
7935 SetColumnOffset(dragging_column, x);
7936 }
7937 }
7938 columns->IsBeingResized = is_being_resized;
7939
7940 window->DC.ColumnsSet = NULL;
7941 window->DC.ColumnsOffset.x = 0.0f;
7942 window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
7943 }
7944
7945 // [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)7946 void ImGui::Columns(int columns_count, const char* id, bool border)
7947 {
7948 ImGuiWindow* window = GetCurrentWindow();
7949 IM_ASSERT(columns_count >= 1);
7950
7951 ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder);
7952 //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior
7953 if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags)
7954 return;
7955
7956 if (window->DC.ColumnsSet != NULL)
7957 EndColumns();
7958
7959 if (columns_count != 1)
7960 BeginColumns(id, columns_count, flags);
7961 }
7962
7963 //-----------------------------------------------------------------------------
7964 // [SECTION] DRAG AND DROP
7965 //-----------------------------------------------------------------------------
7966
ClearDragDrop()7967 void ImGui::ClearDragDrop()
7968 {
7969 ImGuiContext& g = *GImGui;
7970 g.DragDropActive = false;
7971 g.DragDropPayload.Clear();
7972 g.DragDropAcceptFlags = 0;
7973 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
7974 g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
7975 g.DragDropAcceptFrameCount = -1;
7976
7977 g.DragDropPayloadBufHeap.clear();
7978 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
7979 }
7980
7981 // Call when current ID is active.
7982 // 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)7983 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
7984 {
7985 ImGuiContext& g = *GImGui;
7986 ImGuiWindow* window = g.CurrentWindow;
7987
7988 bool source_drag_active = false;
7989 ImGuiID source_id = 0;
7990 ImGuiID source_parent_id = 0;
7991 int mouse_button = 0;
7992 if (!(flags & ImGuiDragDropFlags_SourceExtern))
7993 {
7994 source_id = window->DC.LastItemId;
7995 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case
7996 return false;
7997 if (g.IO.MouseDown[mouse_button] == false)
7998 return false;
7999
8000 if (source_id == 0)
8001 {
8002 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
8003 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride.
8004 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
8005 {
8006 IM_ASSERT(0);
8007 return false;
8008 }
8009
8010 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image()
8011 // We build a throwaway ID based on current ID stack + relative AABB of items in window.
8012 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
8013 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
8014 bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0;
8015 if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window))
8016 return false;
8017 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect);
8018 if (is_hovered)
8019 SetHoveredID(source_id);
8020 if (is_hovered && g.IO.MouseClicked[mouse_button])
8021 {
8022 SetActiveID(source_id, window);
8023 FocusWindow(window);
8024 }
8025 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
8026 g.ActiveIdAllowOverlap = is_hovered;
8027 }
8028 else
8029 {
8030 g.ActiveIdAllowOverlap = false;
8031 }
8032 if (g.ActiveId != source_id)
8033 return false;
8034 source_parent_id = window->IDStack.back();
8035 source_drag_active = IsMouseDragging(mouse_button);
8036 }
8037 else
8038 {
8039 window = NULL;
8040 source_id = ImHash("#SourceExtern", 0);
8041 source_drag_active = true;
8042 }
8043
8044 if (source_drag_active)
8045 {
8046 if (!g.DragDropActive)
8047 {
8048 IM_ASSERT(source_id != 0);
8049 ClearDragDrop();
8050 ImGuiPayload& payload = g.DragDropPayload;
8051 payload.SourceId = source_id;
8052 payload.SourceParentId = source_parent_id;
8053 g.DragDropActive = true;
8054 g.DragDropSourceFlags = flags;
8055 g.DragDropMouseButton = mouse_button;
8056 }
8057 g.DragDropSourceFrameCount = g.FrameCount;
8058 g.DragDropWithinSourceOrTarget = true;
8059
8060 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8061 {
8062 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
8063 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
8064 BeginTooltip();
8065 if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
8066 {
8067 ImGuiWindow* tooltip_window = g.CurrentWindow;
8068 tooltip_window->SkipItems = true;
8069 tooltip_window->HiddenFramesRegular = 1;
8070 }
8071 }
8072
8073 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
8074 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
8075
8076 return true;
8077 }
8078 return false;
8079 }
8080
EndDragDropSource()8081 void ImGui::EndDragDropSource()
8082 {
8083 ImGuiContext& g = *GImGui;
8084 IM_ASSERT(g.DragDropActive);
8085 IM_ASSERT(g.DragDropWithinSourceOrTarget && "Not after a BeginDragDropSource()?");
8086
8087 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
8088 EndTooltip();
8089
8090 // Discard the drag if have not called SetDragDropPayload()
8091 if (g.DragDropPayload.DataFrameCount == -1)
8092 ClearDragDrop();
8093 g.DragDropWithinSourceOrTarget = false;
8094 }
8095
8096 // 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)8097 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
8098 {
8099 ImGuiContext& g = *GImGui;
8100 ImGuiPayload& payload = g.DragDropPayload;
8101 if (cond == 0)
8102 cond = ImGuiCond_Always;
8103
8104 IM_ASSERT(type != NULL);
8105 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
8106 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
8107 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
8108 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
8109
8110 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
8111 {
8112 // Copy payload
8113 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
8114 g.DragDropPayloadBufHeap.resize(0);
8115 if (data_size > sizeof(g.DragDropPayloadBufLocal))
8116 {
8117 // Store in heap
8118 g.DragDropPayloadBufHeap.resize((int)data_size);
8119 payload.Data = g.DragDropPayloadBufHeap.Data;
8120 memcpy(payload.Data, data, data_size);
8121 }
8122 else if (data_size > 0)
8123 {
8124 // Store locally
8125 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
8126 payload.Data = g.DragDropPayloadBufLocal;
8127 memcpy(payload.Data, data, data_size);
8128 }
8129 else
8130 {
8131 payload.Data = NULL;
8132 }
8133 payload.DataSize = (int)data_size;
8134 }
8135 payload.DataFrameCount = g.FrameCount;
8136
8137 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
8138 }
8139
BeginDragDropTargetCustom(const ImRect & bb,ImGuiID id)8140 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
8141 {
8142 ImGuiContext& g = *GImGui;
8143 if (!g.DragDropActive)
8144 return false;
8145
8146 ImGuiWindow* window = g.CurrentWindow;
8147 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8148 return false;
8149 IM_ASSERT(id != 0);
8150 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
8151 return false;
8152 if (window->SkipItems)
8153 return false;
8154
8155 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8156 g.DragDropTargetRect = bb;
8157 g.DragDropTargetId = id;
8158 g.DragDropWithinSourceOrTarget = true;
8159 return true;
8160 }
8161
8162 // We don't use BeginDragDropTargetCustom() and duplicate its code because:
8163 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
8164 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
8165 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
BeginDragDropTarget()8166 bool ImGui::BeginDragDropTarget()
8167 {
8168 ImGuiContext& g = *GImGui;
8169 if (!g.DragDropActive)
8170 return false;
8171
8172 ImGuiWindow* window = g.CurrentWindow;
8173 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
8174 return false;
8175 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
8176 return false;
8177
8178 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
8179 ImGuiID id = window->DC.LastItemId;
8180 if (id == 0)
8181 id = window->GetIDFromRectangle(display_rect);
8182 if (g.DragDropPayload.SourceId == id)
8183 return false;
8184
8185 IM_ASSERT(g.DragDropWithinSourceOrTarget == false);
8186 g.DragDropTargetRect = display_rect;
8187 g.DragDropTargetId = id;
8188 g.DragDropWithinSourceOrTarget = true;
8189 return true;
8190 }
8191
IsDragDropPayloadBeingAccepted()8192 bool ImGui::IsDragDropPayloadBeingAccepted()
8193 {
8194 ImGuiContext& g = *GImGui;
8195 return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
8196 }
8197
AcceptDragDropPayload(const char * type,ImGuiDragDropFlags flags)8198 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
8199 {
8200 ImGuiContext& g = *GImGui;
8201 ImGuiWindow* window = g.CurrentWindow;
8202 ImGuiPayload& payload = g.DragDropPayload;
8203 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
8204 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ?
8205 if (type != NULL && !payload.IsDataType(type))
8206 return NULL;
8207
8208 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
8209 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
8210 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
8211 ImRect r = g.DragDropTargetRect;
8212 float r_surface = r.GetWidth() * r.GetHeight();
8213 if (r_surface < g.DragDropAcceptIdCurrRectSurface)
8214 {
8215 g.DragDropAcceptFlags = flags;
8216 g.DragDropAcceptIdCurr = g.DragDropTargetId;
8217 g.DragDropAcceptIdCurrRectSurface = r_surface;
8218 }
8219
8220 // Render default drop visuals
8221 payload.Preview = was_accepted_previously;
8222 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame)
8223 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)
8224 {
8225 // FIXME-DRAG: Settle on a proper default visuals for drop target.
8226 r.Expand(3.5f);
8227 bool push_clip_rect = !window->ClipRect.Contains(r);
8228 if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1));
8229 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f);
8230 if (push_clip_rect) window->DrawList->PopClipRect();
8231 }
8232
8233 g.DragDropAcceptFrameCount = g.FrameCount;
8234 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()
8235 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
8236 return NULL;
8237
8238 return &payload;
8239 }
8240
8241 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later.
EndDragDropTarget()8242 void ImGui::EndDragDropTarget()
8243 {
8244 ImGuiContext& g = *GImGui;
8245 IM_ASSERT(g.DragDropActive);
8246 IM_ASSERT(g.DragDropWithinSourceOrTarget);
8247 g.DragDropWithinSourceOrTarget = false;
8248 }
8249
8250 //-----------------------------------------------------------------------------
8251 // [SECTION] LOGGING/CAPTURING
8252 //-----------------------------------------------------------------------------
8253
8254 // Pass text data straight to log (without being displayed)
LogText(const char * fmt,...)8255 void ImGui::LogText(const char* fmt, ...)
8256 {
8257 ImGuiContext& g = *GImGui;
8258 if (!g.LogEnabled)
8259 return;
8260
8261 va_list args;
8262 va_start(args, fmt);
8263 if (g.LogFile)
8264 vfprintf(g.LogFile, fmt, args);
8265 else
8266 g.LogClipboard.appendfv(fmt, args);
8267 va_end(args);
8268 }
8269
8270 // Internal version that takes a position to decide on newline placement and pad items according to their depth.
8271 // We split text into individual lines to add current tree level padding
LogRenderedText(const ImVec2 * ref_pos,const char * text,const char * text_end)8272 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
8273 {
8274 ImGuiContext& g = *GImGui;
8275 ImGuiWindow* window = g.CurrentWindow;
8276
8277 if (!text_end)
8278 text_end = FindRenderedTextEnd(text, text_end);
8279
8280 const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1);
8281 if (ref_pos)
8282 window->DC.LogLinePosY = ref_pos->y;
8283
8284 const char* text_remaining = text;
8285 if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth
8286 g.LogStartDepth = window->DC.TreeDepth;
8287 const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth);
8288 for (;;)
8289 {
8290 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry.
8291 const char* line_end = text_remaining;
8292 while (line_end < text_end)
8293 if (*line_end == '\n')
8294 break;
8295 else
8296 line_end++;
8297 if (line_end >= text_end)
8298 line_end = NULL;
8299
8300 const bool is_first_line = (text == text_remaining);
8301 bool is_last_line = false;
8302 if (line_end == NULL)
8303 {
8304 is_last_line = true;
8305 line_end = text_end;
8306 }
8307 if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0))
8308 {
8309 const int char_count = (int)(line_end - text_remaining);
8310 if (log_new_line || !is_first_line)
8311 LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining);
8312 else
8313 LogText(" %.*s", char_count, text_remaining);
8314 }
8315
8316 if (is_last_line)
8317 break;
8318 text_remaining = line_end + 1;
8319 }
8320 }
8321
8322 // Start logging ImGui output to TTY
LogToTTY(int max_depth)8323 void ImGui::LogToTTY(int max_depth)
8324 {
8325 ImGuiContext& g = *GImGui;
8326 if (g.LogEnabled)
8327 return;
8328 ImGuiWindow* window = g.CurrentWindow;
8329
8330 IM_ASSERT(g.LogFile == NULL);
8331 g.LogFile = stdout;
8332 g.LogEnabled = true;
8333 g.LogStartDepth = window->DC.TreeDepth;
8334 if (max_depth >= 0)
8335 g.LogAutoExpandMaxDepth = max_depth;
8336 }
8337
8338 // Start logging ImGui output to given file
LogToFile(int max_depth,const char * filename)8339 void ImGui::LogToFile(int max_depth, const char* filename)
8340 {
8341 ImGuiContext& g = *GImGui;
8342 if (g.LogEnabled)
8343 return;
8344 ImGuiWindow* window = g.CurrentWindow;
8345
8346 if (!filename)
8347 {
8348 filename = g.IO.LogFilename;
8349 if (!filename)
8350 return;
8351 }
8352
8353 IM_ASSERT(g.LogFile == NULL);
8354 g.LogFile = ImFileOpen(filename, "ab");
8355 if (!g.LogFile)
8356 {
8357 IM_ASSERT(g.LogFile != NULL); // Consider this an error
8358 return;
8359 }
8360 g.LogEnabled = true;
8361 g.LogStartDepth = window->DC.TreeDepth;
8362 if (max_depth >= 0)
8363 g.LogAutoExpandMaxDepth = max_depth;
8364 }
8365
8366 // Start logging ImGui output to clipboard
LogToClipboard(int max_depth)8367 void ImGui::LogToClipboard(int max_depth)
8368 {
8369 ImGuiContext& g = *GImGui;
8370 if (g.LogEnabled)
8371 return;
8372 ImGuiWindow* window = g.CurrentWindow;
8373
8374 IM_ASSERT(g.LogFile == NULL);
8375 g.LogFile = NULL;
8376 g.LogEnabled = true;
8377 g.LogStartDepth = window->DC.TreeDepth;
8378 if (max_depth >= 0)
8379 g.LogAutoExpandMaxDepth = max_depth;
8380 }
8381
LogFinish()8382 void ImGui::LogFinish()
8383 {
8384 ImGuiContext& g = *GImGui;
8385 if (!g.LogEnabled)
8386 return;
8387
8388 LogText(IM_NEWLINE);
8389 if (g.LogFile != NULL)
8390 {
8391 if (g.LogFile == stdout)
8392 fflush(g.LogFile);
8393 else
8394 fclose(g.LogFile);
8395 g.LogFile = NULL;
8396 }
8397 if (g.LogClipboard.size() > 1)
8398 {
8399 SetClipboardText(g.LogClipboard.begin());
8400 g.LogClipboard.clear();
8401 }
8402 g.LogEnabled = false;
8403 }
8404
8405 // Helper to display logging buttons
LogButtons()8406 void ImGui::LogButtons()
8407 {
8408 ImGuiContext& g = *GImGui;
8409
8410 PushID("LogButtons");
8411 const bool log_to_tty = Button("Log To TTY"); SameLine();
8412 const bool log_to_file = Button("Log To File"); SameLine();
8413 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
8414 PushItemWidth(80.0f);
8415 PushAllowKeyboardFocus(false);
8416 SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL);
8417 PopAllowKeyboardFocus();
8418 PopItemWidth();
8419 PopID();
8420
8421 // Start logging at the end of the function so that the buttons don't appear in the log
8422 if (log_to_tty)
8423 LogToTTY(g.LogAutoExpandMaxDepth);
8424 if (log_to_file)
8425 LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename);
8426 if (log_to_clipboard)
8427 LogToClipboard(g.LogAutoExpandMaxDepth);
8428 }
8429
8430 //-----------------------------------------------------------------------------
8431 // [SECTION] SETTINGS
8432 //-----------------------------------------------------------------------------
8433
MarkIniSettingsDirty()8434 void ImGui::MarkIniSettingsDirty()
8435 {
8436 ImGuiContext& g = *GImGui;
8437 if (g.SettingsDirtyTimer <= 0.0f)
8438 g.SettingsDirtyTimer = g.IO.IniSavingRate;
8439 }
8440
MarkIniSettingsDirty(ImGuiWindow * window)8441 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
8442 {
8443 ImGuiContext& g = *GImGui;
8444 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
8445 if (g.SettingsDirtyTimer <= 0.0f)
8446 g.SettingsDirtyTimer = g.IO.IniSavingRate;
8447 }
8448
CreateNewWindowSettings(const char * name)8449 static ImGuiWindowSettings* CreateNewWindowSettings(const char* name)
8450 {
8451 ImGuiContext& g = *GImGui;
8452 g.SettingsWindows.push_back(ImGuiWindowSettings());
8453 ImGuiWindowSettings* settings = &g.SettingsWindows.back();
8454 settings->Name = ImStrdup(name);
8455 settings->ID = ImHash(name, 0);
8456 return settings;
8457 }
8458
FindWindowSettings(ImGuiID id)8459 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id)
8460 {
8461 ImGuiContext& g = *GImGui;
8462 for (int i = 0; i != g.SettingsWindows.Size; i++)
8463 if (g.SettingsWindows[i].ID == id)
8464 return &g.SettingsWindows[i];
8465 return NULL;
8466 }
8467
LoadIniSettingsFromDisk(const char * ini_filename)8468 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
8469 {
8470 size_t file_data_size = 0;
8471 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
8472 if (!file_data)
8473 return;
8474 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
8475 ImGui::MemFree(file_data);
8476 }
8477
FindSettingsHandler(const char * type_name)8478 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
8479 {
8480 ImGuiContext& g = *GImGui;
8481 const ImGuiID type_hash = ImHash(type_name, 0, 0);
8482 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8483 if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
8484 return &g.SettingsHandlers[handler_n];
8485 return NULL;
8486 }
8487
8488 // Zero-tolerance, no error reporting, cheap .ini parsing
LoadIniSettingsFromMemory(const char * ini_data,size_t ini_size)8489 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
8490 {
8491 ImGuiContext& g = *GImGui;
8492 IM_ASSERT(g.Initialized);
8493 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
8494
8495 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
8496 // 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..
8497 if (ini_size == 0)
8498 ini_size = strlen(ini_data);
8499 char* buf = (char*)ImGui::MemAlloc(ini_size + 1);
8500 char* buf_end = buf + ini_size;
8501 memcpy(buf, ini_data, ini_size);
8502 buf[ini_size] = 0;
8503
8504 void* entry_data = NULL;
8505 ImGuiSettingsHandler* entry_handler = NULL;
8506
8507 char* line_end = NULL;
8508 for (char* line = buf; line < buf_end; line = line_end + 1)
8509 {
8510 // Skip new lines markers, then find end of the line
8511 while (*line == '\n' || *line == '\r')
8512 line++;
8513 line_end = line;
8514 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
8515 line_end++;
8516 line_end[0] = 0;
8517 if (line[0] == ';')
8518 continue;
8519 if (line[0] == '[' && line_end > line && line_end[-1] == ']')
8520 {
8521 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
8522 line_end[-1] = 0;
8523 const char* name_end = line_end - 1;
8524 const char* type_start = line + 1;
8525 char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']');
8526 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
8527 if (!type_end || !name_start)
8528 {
8529 name_start = type_start; // Import legacy entries that have no type
8530 type_start = "Window";
8531 }
8532 else
8533 {
8534 *type_end = 0; // Overwrite first ']'
8535 name_start++; // Skip second '['
8536 }
8537 entry_handler = FindSettingsHandler(type_start);
8538 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
8539 }
8540 else if (entry_handler != NULL && entry_data != NULL)
8541 {
8542 // Let type handler parse the line
8543 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
8544 }
8545 }
8546 ImGui::MemFree(buf);
8547 g.SettingsLoaded = true;
8548 }
8549
SaveIniSettingsToDisk(const char * ini_filename)8550 void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
8551 {
8552 ImGuiContext& g = *GImGui;
8553 g.SettingsDirtyTimer = 0.0f;
8554 if (!ini_filename)
8555 return;
8556
8557 size_t ini_data_size = 0;
8558 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
8559 FILE* f = ImFileOpen(ini_filename, "wt");
8560 if (!f)
8561 return;
8562 fwrite(ini_data, sizeof(char), ini_data_size, f);
8563 fclose(f);
8564 }
8565
8566 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
SaveIniSettingsToMemory(size_t * out_size)8567 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
8568 {
8569 ImGuiContext& g = *GImGui;
8570 g.SettingsDirtyTimer = 0.0f;
8571 g.SettingsIniData.Buf.resize(0);
8572 g.SettingsIniData.Buf.push_back(0);
8573 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
8574 {
8575 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n];
8576 handler->WriteAllFn(&g, handler, &g.SettingsIniData);
8577 }
8578 if (out_size)
8579 *out_size = (size_t)g.SettingsIniData.size();
8580 return g.SettingsIniData.c_str();
8581 }
8582
SettingsHandlerWindow_ReadOpen(ImGuiContext *,ImGuiSettingsHandler *,const char * name)8583 static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
8584 {
8585 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
8586 if (!settings)
8587 settings = CreateNewWindowSettings(name);
8588 return (void*)settings;
8589 }
8590
SettingsHandlerWindow_ReadLine(ImGuiContext *,ImGuiSettingsHandler *,void * entry,const char * line)8591 static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
8592 {
8593 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
8594 float x, y;
8595 int i;
8596 if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
8597 else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
8598 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
8599 }
8600
SettingsHandlerWindow_WriteAll(ImGuiContext * imgui_ctx,ImGuiSettingsHandler * handler,ImGuiTextBuffer * buf)8601 static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
8602 {
8603 // Gather data from windows that were active during this session
8604 // (if a window wasn't opened in this session we preserve its settings)
8605 ImGuiContext& g = *imgui_ctx;
8606 for (int i = 0; i != g.Windows.Size; i++)
8607 {
8608 ImGuiWindow* window = g.Windows[i];
8609 if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
8610 continue;
8611
8612 ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
8613 if (!settings)
8614 {
8615 settings = CreateNewWindowSettings(window->Name);
8616 window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
8617 }
8618 IM_ASSERT(settings->ID == window->ID);
8619 settings->Pos = window->Pos;
8620 settings->Size = window->SizeFull;
8621 settings->Collapsed = window->Collapsed;
8622 }
8623
8624 // Write to text buffer
8625 buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve
8626 for (int i = 0; i != g.SettingsWindows.Size; i++)
8627 {
8628 const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
8629 if (settings->Pos.x == FLT_MAX)
8630 continue;
8631 const char* name = settings->Name;
8632 if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
8633 name = p;
8634 buf->appendf("[%s][%s]\n", handler->TypeName, name);
8635 buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
8636 buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
8637 buf->appendf("Collapsed=%d\n", settings->Collapsed);
8638 buf->appendf("\n");
8639 }
8640 }
8641
8642 //-----------------------------------------------------------------------------
8643 // [SECTION] PLATFORM DEPENDENT HELPERS
8644 //-----------------------------------------------------------------------------
8645
8646 #if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS))
8647 #ifndef WIN32_LEAN_AND_MEAN
8648 #define WIN32_LEAN_AND_MEAN
8649 #endif
8650 #ifndef __MINGW32__
8651 #include <Windows.h>
8652 #else
8653 #include <windows.h>
8654 #endif
8655 #endif
8656
8657 // Win32 API clipboard implementation
8658 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
8659
8660 #ifdef _MSC_VER
8661 #pragma comment(lib, "user32")
8662 #endif
8663
GetClipboardTextFn_DefaultImpl(void *)8664 static const char* GetClipboardTextFn_DefaultImpl(void*)
8665 {
8666 static ImVector<char> buf_local;
8667 buf_local.clear();
8668 if (!::OpenClipboard(NULL))
8669 return NULL;
8670 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
8671 if (wbuf_handle == NULL)
8672 {
8673 ::CloseClipboard();
8674 return NULL;
8675 }
8676 if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle))
8677 {
8678 int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1;
8679 buf_local.resize(buf_len);
8680 ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL);
8681 }
8682 ::GlobalUnlock(wbuf_handle);
8683 ::CloseClipboard();
8684 return buf_local.Data;
8685 }
8686
SetClipboardTextFn_DefaultImpl(void *,const char * text)8687 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8688 {
8689 if (!::OpenClipboard(NULL))
8690 return;
8691 const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1;
8692 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar));
8693 if (wbuf_handle == NULL)
8694 {
8695 ::CloseClipboard();
8696 return;
8697 }
8698 ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle);
8699 ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL);
8700 ::GlobalUnlock(wbuf_handle);
8701 ::EmptyClipboard();
8702 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
8703 ::GlobalFree(wbuf_handle);
8704 ::CloseClipboard();
8705 }
8706
8707 #else
8708
8709 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
GetClipboardTextFn_DefaultImpl(void *)8710 static const char* GetClipboardTextFn_DefaultImpl(void*)
8711 {
8712 ImGuiContext& g = *GImGui;
8713 return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin();
8714 }
8715
8716 // Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers
SetClipboardTextFn_DefaultImpl(void *,const char * text)8717 static void SetClipboardTextFn_DefaultImpl(void*, const char* text)
8718 {
8719 ImGuiContext& g = *GImGui;
8720 g.PrivateClipboard.clear();
8721 const char* text_end = text + strlen(text);
8722 g.PrivateClipboard.resize((int)(text_end - text) + 1);
8723 memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text));
8724 g.PrivateClipboard[(int)(text_end - text)] = 0;
8725 }
8726
8727 #endif
8728
8729 // Win32 API IME support (for Asian languages, etc.)
8730 #if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
8731
8732 #include <imm.h>
8733 #ifdef _MSC_VER
8734 #pragma comment(lib, "imm32")
8735 #endif
8736
ImeSetInputScreenPosFn_DefaultImpl(int x,int y)8737 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y)
8738 {
8739 // Notify OS Input Method Editor of text input position
8740 if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle)
8741 if (HIMC himc = ::ImmGetContext(hwnd))
8742 {
8743 COMPOSITIONFORM cf;
8744 cf.ptCurrentPos.x = x;
8745 cf.ptCurrentPos.y = y;
8746 cf.dwStyle = CFS_FORCE_POSITION;
8747 ::ImmSetCompositionWindow(himc, &cf);
8748 ::ImmReleaseContext(hwnd, himc);
8749 }
8750 }
8751
8752 #else
8753
ImeSetInputScreenPosFn_DefaultImpl(int,int)8754 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {}
8755
8756 #endif
8757
8758 //-----------------------------------------------------------------------------
8759 // [SECTION] METRICS/DEBUG WINDOW
8760 //-----------------------------------------------------------------------------
8761
ShowMetricsWindow(bool * p_open)8762 void ImGui::ShowMetricsWindow(bool* p_open)
8763 {
8764 if (!ImGui::Begin("ImGui Metrics", p_open))
8765 {
8766 ImGui::End();
8767 return;
8768 }
8769 static bool show_draw_cmd_clip_rects = true;
8770 static bool show_window_begin_order = false;
8771 ImGuiIO& io = ImGui::GetIO();
8772 ImGui::Text("Dear ImGui %s", ImGui::GetVersion());
8773 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
8774 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
8775 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows);
8776 ImGui::Text("%d allocations", io.MetricsActiveAllocations);
8777 ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects);
8778 ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order);
8779
8780 ImGui::Separator();
8781
8782 struct Funcs
8783 {
8784 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label)
8785 {
8786 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);
8787 if (draw_list == ImGui::GetWindowDrawList())
8788 {
8789 ImGui::SameLine();
8790 ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
8791 if (node_open) ImGui::TreePop();
8792 return;
8793 }
8794
8795 ImDrawList* overlay_draw_list = GetOverlayDrawList(); // Render additional visuals into the top-most draw list
8796 if (window && IsItemHovered())
8797 overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
8798 if (!node_open)
8799 return;
8800
8801 int elem_offset = 0;
8802 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++)
8803 {
8804 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0)
8805 continue;
8806 if (pcmd->UserCallback)
8807 {
8808 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
8809 continue;
8810 }
8811 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
8812 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);
8813 if (show_draw_cmd_clip_rects && ImGui::IsItemHovered())
8814 {
8815 ImRect clip_rect = pcmd->ClipRect;
8816 ImRect vtxs_rect;
8817 for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++)
8818 vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos);
8819 clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255));
8820 vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255));
8821 }
8822 if (!pcmd_node_open)
8823 continue;
8824
8825 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
8826 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
8827 while (clipper.Step())
8828 for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++)
8829 {
8830 char buf[300];
8831 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf);
8832 ImVec2 triangles_pos[3];
8833 for (int n = 0; n < 3; n++, vtx_i++)
8834 {
8835 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i];
8836 triangles_pos[n] = v.pos;
8837 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);
8838 }
8839 ImGui::Selectable(buf, false);
8840 if (ImGui::IsItemHovered())
8841 {
8842 ImDrawListFlags backup_flags = overlay_draw_list->Flags;
8843 overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles.
8844 overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f);
8845 overlay_draw_list->Flags = backup_flags;
8846 }
8847 }
8848 ImGui::TreePop();
8849 }
8850 ImGui::TreePop();
8851 }
8852
8853 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label)
8854 {
8855 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size))
8856 return;
8857 for (int i = 0; i < windows.Size; i++)
8858 Funcs::NodeWindow(windows[i], "Window");
8859 ImGui::TreePop();
8860 }
8861
8862 static void NodeWindow(ImGuiWindow* window, const char* label)
8863 {
8864 if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window))
8865 return;
8866 ImGuiWindowFlags flags = window->Flags;
8867 NodeDrawList(window, window->DrawList, "DrawList");
8868 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);
8869 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s..)", flags,
8870 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
8871 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
8872 (flags & ImGuiWindowFlags_NoInputs) ? "NoInputs":"", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
8873 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window));
8874 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
8875 ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems);
8876 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask);
8877 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
8878 if (!window->NavRectRel[0].IsInverted())
8879 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);
8880 else
8881 ImGui::BulletText("NavRectRel[0]: <None>");
8882 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
8883 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
8884 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
8885 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
8886 {
8887 for (int n = 0; n < window->ColumnsStorage.Size; n++)
8888 {
8889 const ImGuiColumnsSet* columns = &window->ColumnsStorage[n];
8890 if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
8891 {
8892 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX);
8893 for (int column_n = 0; column_n < columns->Columns.Size; column_n++)
8894 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm));
8895 ImGui::TreePop();
8896 }
8897 }
8898 ImGui::TreePop();
8899 }
8900 ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair));
8901 ImGui::TreePop();
8902 }
8903 };
8904
8905 // Access private state, we are going to display the draw lists from last frame
8906 ImGuiContext& g = *GImGui;
8907 Funcs::NodeWindows(g.Windows, "Windows");
8908 if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size))
8909 {
8910 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++)
8911 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
8912 ImGui::TreePop();
8913 }
8914 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
8915 {
8916 for (int i = 0; i < g.OpenPopupStack.Size; i++)
8917 {
8918 ImGuiWindow* window = g.OpenPopupStack[i].Window;
8919 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" : "");
8920 }
8921 ImGui::TreePop();
8922 }
8923 if (ImGui::TreeNode("Internal state"))
8924 {
8925 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
8926 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
8927 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
8928 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
8929 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]);
8930 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
8931 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
8932 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
8933 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
8934 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]);
8935 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
8936 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId);
8937 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover);
8938 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
8939 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
8940 ImGui::TreePop();
8941 }
8942
8943
8944 if (g.IO.KeyCtrl && show_window_begin_order)
8945 {
8946 for (int n = 0; n < g.Windows.Size; n++)
8947 {
8948 ImGuiWindow* window = g.Windows[n];
8949 if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive)
8950 continue;
8951 char buf[32];
8952 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
8953 float font_size = ImGui::GetFontSize() * 2;
8954 ImDrawList* overlay_draw_list = GetOverlayDrawList();
8955 overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
8956 overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf);
8957 }
8958 }
8959 ImGui::End();
8960 }
8961
8962 //-----------------------------------------------------------------------------
8963
8964 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
8965 // 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.
8966 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL
8967 #include "imgui_user.inl"
8968 #endif
8969
8970 //-----------------------------------------------------------------------------
8971