imgui.cpp (507372B)
1// dear imgui, v1.76 2// (main code and documentation) 3 4// Help: 5// - Read FAQ at http://dearimgui.org/faq 6// - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. 7// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. All applications in examples/ are doing that. 8// Read imgui.cpp for details, links and comments. 9 10// Resources: 11// - FAQ http://dearimgui.org/faq 12// - Homepage & latest https://github.com/ocornut/imgui 13// - Releases & changelog https://github.com/ocornut/imgui/releases 14// - Gallery https://github.com/ocornut/imgui/issues/3075 (please post your screenshots/video there!) 15// - Glossary https://github.com/ocornut/imgui/wiki/Glossary 16// - Wiki https://github.com/ocornut/imgui/wiki 17// - Issues & support https://github.com/ocornut/imgui/issues 18 19// Developed by Omar Cornut and every direct or indirect contributors to the GitHub. 20// See LICENSE.txt for copyright and licensing details (standard MIT License). 21// This library is free but I need your support to sustain development and maintenance. 22// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.org". 23// Individuals: you can support continued development via donations. See docs/README or web page. 24 25// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. 26// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without 27// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't 28// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you 29// to a better solution or official support for them. 30 31/* 32 33Index of this file: 34 35DOCUMENTATION 36 37- MISSION STATEMENT 38- END-USER GUIDE 39- PROGRAMMER GUIDE 40 - READ FIRST 41 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 42 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 43 - HOW A SIMPLE APPLICATION MAY LOOK LIKE 44 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE 45 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 46- API BREAKING CHANGES (read me when you update!) 47- FREQUENTLY ASKED QUESTIONS (FAQ) 48 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer) 49 50CODE 51(search for "[SECTION]" in the code to find them) 52 53// [SECTION] INCLUDES 54// [SECTION] FORWARD DECLARATIONS 55// [SECTION] CONTEXT AND MEMORY ALLOCATORS 56// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) 57// [SECTION] MISC HELPERS/UTILITIES (Geometry functions) 58// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) 59// [SECTION] MISC HELPERS/UTILITIES (File functions) 60// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) 61// [SECTION] MISC HELPERS/UTILITIES (Color functions) 62// [SECTION] ImGuiStorage 63// [SECTION] ImGuiTextFilter 64// [SECTION] ImGuiTextBuffer 65// [SECTION] ImGuiListClipper 66// [SECTION] STYLING 67// [SECTION] RENDER HELPERS 68// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) 69// [SECTION] ERROR CHECKING 70// [SECTION] LAYOUT 71// [SECTION] SCROLLING 72// [SECTION] TOOLTIPS 73// [SECTION] POPUPS 74// [SECTION] KEYBOARD/GAMEPAD NAVIGATION 75// [SECTION] DRAG AND DROP 76// [SECTION] LOGGING/CAPTURING 77// [SECTION] SETTINGS 78// [SECTION] PLATFORM DEPENDENT HELPERS 79// [SECTION] METRICS/DEBUG WINDOW 80 81*/ 82 83//----------------------------------------------------------------------------- 84// DOCUMENTATION 85//----------------------------------------------------------------------------- 86 87/* 88 89 MISSION STATEMENT 90 ================= 91 92 - Easy to use to create code-driven and data-driven tools. 93 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. 94 - Easy to hack and improve. 95 - Minimize screen real-estate usage. 96 - Minimize setup and maintenance. 97 - Minimize state storage on user side. 98 - Portable, minimize dependencies, run on target (consoles, phones, etc.). 99 - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,. 100 opening a tree node for the first time, etc. but a typical frame should not allocate anything). 101 102 Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: 103 - Doesn't look fancy, doesn't animate. 104 - Limited layout features, intricate layouts are typically crafted in code. 105 106 107 END-USER GUIDE 108 ============== 109 110 - Double-click on title bar to collapse window. 111 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin(). 112 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents). 113 - Click and drag on any empty space to move window. 114 - TAB/SHIFT+TAB to cycle through keyboard editable fields. 115 - CTRL+Click on a slider or drag box to input value as text. 116 - Use mouse wheel to scroll. 117 - Text editor: 118 - Hold SHIFT or use mouse to select text. 119 - CTRL+Left/Right to word jump. 120 - CTRL+Shift+Left/Right to select words. 121 - CTRL+A our Double-Click to select all. 122 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ 123 - CTRL+Z,CTRL+Y to undo/redo. 124 - ESCAPE to revert text to its original value. 125 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) 126 - Controls are automatically adjusted for OSX to match standard OSX text editing operations. 127 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. 128 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW 129 130 131 PROGRAMMER GUIDE 132 ================ 133 134 READ FIRST 135 ---------- 136 - Remember to read the FAQ (https://www.dearimgui.org/faq) 137 - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction 138 or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. 139 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. 140 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. 141 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). 142 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in the FAQ. 143 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. 144 For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI, 145 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. 146 - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. 147 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. 148 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). 149 If you get an assert, read the messages and comments around the assert. 150 - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. 151 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. 152 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. 153 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. 154 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). 155 156 157 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 158 ---------------------------------------------- 159 - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) 160 - Or maintain your own branch where you have imconfig.h modified. 161 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. 162 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed 163 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will 164 likely be a comment about it. Please report any issue to the GitHub page! 165 - Try to keep your copy of dear imgui reasonably up to date. 166 167 168 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 169 --------------------------------------------------------------- 170 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. 171 - In the majority of cases you should be able to use unmodified back-ends files available in the examples/ folder. 172 - Add the Dear ImGui source files to your projects or using your preferred build system. 173 It is recommended you build and statically link the .cpp files as part of your project and NOT as shared library (DLL). 174 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types. 175 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. 176 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. 177 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" 178 phases of your own application. All rendering information are stored into command-lists that you will retrieve after calling ImGui::Render(). 179 - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code. 180 - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder. 181 182 183 HOW A SIMPLE APPLICATION MAY LOOK LIKE 184 -------------------------------------- 185 EXHIBIT 1: USING THE EXAMPLE BINDINGS (= imgui_impl_XXX.cpp files from the examples/ folder). 186 The sub-folders in examples/ contains examples applications following this structure. 187 188 // Application init: create a dear imgui context, setup some options, load fonts 189 ImGui::CreateContext(); 190 ImGuiIO& io = ImGui::GetIO(); 191 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. 192 // TODO: Fill optional fields of the io structure later. 193 // TODO: Load TTF/OTF fonts if you don't want to use the default font. 194 195 // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp) 196 ImGui_ImplWin32_Init(hwnd); 197 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); 198 199 // Application main loop 200 while (true) 201 { 202 // Feed inputs to dear imgui, start new frame 203 ImGui_ImplDX11_NewFrame(); 204 ImGui_ImplWin32_NewFrame(); 205 ImGui::NewFrame(); 206 207 // Any application code here 208 ImGui::Text("Hello, world!"); 209 210 // Render dear imgui into screen 211 ImGui::Render(); 212 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); 213 g_pSwapChain->Present(1, 0); 214 } 215 216 // Shutdown 217 ImGui_ImplDX11_Shutdown(); 218 ImGui_ImplWin32_Shutdown(); 219 ImGui::DestroyContext(); 220 221 EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE 222 223 // Application init: create a dear imgui context, setup some options, load fonts 224 ImGui::CreateContext(); 225 ImGuiIO& io = ImGui::GetIO(); 226 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. 227 // TODO: Fill optional fields of the io structure later. 228 // TODO: Load TTF/OTF fonts if you don't want to use the default font. 229 230 // Build and load the texture atlas into a texture 231 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer) 232 int width, height; 233 unsigned char* pixels = NULL; 234 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 235 236 // At this point you've got the texture data and you need to upload that your your graphic system: 237 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. 238 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID. 239 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) 240 io.Fonts->TexID = (void*)texture; 241 242 // Application main loop 243 while (true) 244 { 245 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc. 246 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform bindings) 247 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) 248 io.DisplaySize.x = 1920.0f; // set the current display width 249 io.DisplaySize.y = 1280.0f; // set the current display height here 250 io.MousePos = my_mouse_pos; // set the mouse position 251 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states 252 io.MouseDown[1] = my_mouse_buttons[1]; 253 254 // Call NewFrame(), after this point you can use ImGui::* functions anytime 255 // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use Dear ImGui everywhere) 256 ImGui::NewFrame(); 257 258 // Most of your application code here 259 ImGui::Text("Hello, world!"); 260 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); 261 MyGameRender(); // may use any Dear ImGui functions as well! 262 263 // Render dear imgui, swap buffers 264 // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code) 265 ImGui::EndFrame(); 266 ImGui::Render(); 267 ImDrawData* draw_data = ImGui::GetDrawData(); 268 MyImGuiRenderFunction(draw_data); 269 SwapBuffers(); 270 } 271 272 // Shutdown 273 ImGui::DestroyContext(); 274 275 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest your application, 276 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! 277 Please read the FAQ and example applications for details about this! 278 279 280 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE 281 --------------------------------------------- 282 The bindings in impl_impl_XXX.cpp files contains many working implementations of a rendering function. 283 284 void void MyImGuiRenderFunction(ImDrawData* draw_data) 285 { 286 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled 287 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize 288 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize 289 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. 290 for (int n = 0; n < draw_data->CmdListsCount; n++) 291 { 292 const ImDrawList* cmd_list = draw_data->CmdLists[n]; 293 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui 294 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui 295 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 296 { 297 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 298 if (pcmd->UserCallback) 299 { 300 pcmd->UserCallback(cmd_list, pcmd); 301 } 302 else 303 { 304 // The texture for the draw call is specified by pcmd->TextureId. 305 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. 306 MyEngineBindTexture((MyTexture*)pcmd->TextureId); 307 308 // We are using scissoring to clip some objects. All low-level graphics API should supports it. 309 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches 310 // (some elements visible outside their bounds) but you can fix that once everything else works! 311 // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) 312 // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. 313 // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), 314 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. 315 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) 316 ImVec2 pos = draw_data->DisplayPos; 317 MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); 318 319 // Render 'pcmd->ElemCount/3' indexed triangles. 320 // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices. 321 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); 322 } 323 idx_buffer += pcmd->ElemCount; 324 } 325 } 326 } 327 328 329 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 330 ------------------------------------------ 331 - The gamepad/keyboard navigation is fairly functional and keeps being improved. 332 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse! 333 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 334 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. 335 - Keyboard: 336 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. 337 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. 338 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag 339 will be set. For more advanced uses, you may want to read from: 340 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. 341 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). 342 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. 343 Please reach out if you think the game vs navigation input sharing could be improved. 344 - Gamepad: 345 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. 346 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). 347 Note that io.NavInputs[] is cleared by EndFrame(). 348 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 349 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. 350 - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. 351 Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). 352 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW. 353 - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo 354 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. 355 - Mouse: 356 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. 357 - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. 358 - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. 359 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. 360 When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved. 361 When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. 362 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) 363 (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want 364 to set a boolean to ignore your other external mouse positions until the external source is moved again.) 365 366 367 API BREAKING CHANGES 368 ==================== 369 370 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. 371 Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. 372 When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. 373 You can read releases logs https://github.com/ocornut/imgui/releases for more details. 374 375 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more. 376 - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead. 377 - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value. 378 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017): 379 - ShowTestWindow() -> use ShowDemoWindow() 380 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) 381 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) 382 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) 383 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() 384 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg 385 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding 386 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap 387 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS 388 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API. 389 - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it). 390 - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert. 391 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. 392 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency. 393 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): 394 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed 395 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) 396 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding() 397 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f) 398 - ImFont::Glyph -> use ImFontGlyph 399 - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function. 400 if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix. 401 The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay). 402 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you. 403 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete). 404 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete). 405 - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71. 406 - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have 407 overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering. 408 This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows. 409 Please reach out if you are affected. 410 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). 411 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c). 412 - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now. 413 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). 414 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). 415 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). 416 - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value! 417 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). 418 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! 419 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). 420 - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects. 421 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. 422 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. 423 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). 424 - 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. 425 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. 426 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) 427 - 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. 428 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED. 429 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions. 430 - 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). 431 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). 432 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). 433 - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature. 434 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. 435 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. 436 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). 437 - 2018/06/08 (1.62) - examples: the imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.). 438 old bindings will still work as is, however prefer using the separated bindings as they will be updated to support multi-viewports. 439 when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call. 440 in particular, note that old bindings called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function. 441 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. 442 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. 443 - 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. 444 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. 445 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. 446 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. 447 - 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", 448 consistent with other functions. Kept redirection functions (will obsolete). 449 - 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. 450 - 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). 451 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. 452 - 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. 453 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. 454 - 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. 455 - 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. 456 - 2018/02/07 (1.60) - reorganized context handling to be more explicit, 457 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. 458 - removed Shutdown() function, as DestroyContext() serve this purpose. 459 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. 460 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. 461 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. 462 - 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. 463 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). 464 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). 465 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. 466 - 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. 467 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). 468 - 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 469 - 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. 470 - 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. 471 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). 472 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). 473 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). 474 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). 475 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). 476 - 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. 477 - 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. 478 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. 479 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. 480 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. 481 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. 482 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); 483 - 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. 484 - 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. 485 - 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. 486 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. 487 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly) 488 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow) 489 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior] 490 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! 491 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). 492 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete). 493 - 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). 494 - 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)". 495 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! 496 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). 497 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). 498 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. 499 - 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. 500 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type. 501 - 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. 502 - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete). 503 - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete). 504 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). 505 - 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. 506 - 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. 507 - 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))' 508 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse 509 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. 510 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. 511 - 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(). 512 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. 513 - 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. 514 - 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. 515 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. 516 If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. 517 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: 518 ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); } 519 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. 520 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). 521 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. 522 - 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). 523 - 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. 524 - 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). 525 - 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) 526 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). 527 - 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. 528 - 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. 529 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. 530 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. 531 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. 532 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. 533 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! 534 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize 535 - 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. 536 - 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 537 - 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. 538 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. 539 - 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. 540 this necessary change will break your rendering function! the fix should be very easy. sorry for that :( 541 - 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. 542 - the signature of the io.RenderDrawListsFn handler has changed! 543 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) 544 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). 545 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' 546 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. 547 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. 548 - 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. 549 - 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! 550 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! 551 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. 552 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). 553 - 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. 554 - 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 555 - 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! 556 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). 557 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). 558 - 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. 559 - 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. 560 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). 561 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. 562 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API 563 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. 564 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. 565 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. 566 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing 567 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. 568 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) 569 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. 570 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. 571 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. 572 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior 573 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() 574 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) 575 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. 576 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. 577 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. 578 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..]; 579 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->TexId = YourTexIdentifier; 580 you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation. 581 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. 582 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) 583 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets 584 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) 585 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) 586 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility 587 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() 588 - 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) 589 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) 590 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() 591 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn 592 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) 593 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite 594 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes 595 596 597 FREQUENTLY ASKED QUESTIONS (FAQ) 598 ================================ 599 600 Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer) 601 Some answers are copied down here to facilitate searching in code. 602 603 Q&A: Basics 604 =========== 605 606 Q: Where is the documentation? 607 A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++. 608 - Run the examples/ and explore them. 609 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. 610 - The demo covers most features of Dear ImGui, so you can read the code and see its output. 611 - See documentation and comments at the top of imgui.cpp + effectively imgui.h. 612 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the 613 examples/ folder to explain how to integrate Dear ImGui with your own engine/application. 614 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. 615 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. 616 - Your programming IDE is your friend, find the type or function declaration to find comments 617 associated to it. 618 619 Q: What is this library called? 620 Q: Which version should I get? 621 >> This library is called "Dear ImGui", please don't call it "ImGui" :) 622 >> See https://www.dearimgui.org/faq 623 624 Q&A: Integration 625 ================ 626 627 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or to my application? 628 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! 629 >> See https://www.dearimgui.org/faq for fully detailed answer. You really want to read this. 630 631 Q. How can I enable keyboard controls? 632 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) 633 Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. 634 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. 635 >> See https://www.dearimgui.org/faq 636 637 Q&A: Usage 638 ---------- 639 640 Q: Why are multiple widgets reacting when I interact with a single one? 641 Q: How can I have multiple widgets with the same label or with an empty label? 642 A: A primer on labels and the ID Stack... 643 644 Dear ImGui internally need to uniquely identify UI elements. 645 Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. 646 Interactive widgets (such as calls to Button buttons) need a unique ID. 647 Unique ID are used internally to track active widgets and occasionally associate state to widgets. 648 Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. 649 650 - Unique ID are often derived from a string label: 651 652 Button("OK"); // Label = "OK", ID = hash of (..., "OK") 653 Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel") 654 655 - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having 656 two buttons labeled "OK" in different windows or different tree locations is fine. 657 We used "..." above to signify whatever was already pushed to the ID stack previously: 658 659 Begin("MyWindow"); 660 Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK") 661 End(); 662 Begin("MyOtherWindow"); 663 Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK") 664 End(); 665 666 - If you have a same ID twice in the same location, you'll have a conflict: 667 668 Button("OK"); 669 Button("OK"); // ID collision! Interacting with either button will trigger the first one. 670 671 Fear not! this is easy to solve and there are many ways to solve it! 672 673 - Solving ID conflict in a simple/local context: 674 When passing a label you can optionally specify extra ID information within string itself. 675 Use "##" to pass a complement to the ID that won't be visible to the end-user. 676 This helps solving the simple collision cases when you know e.g. at compilation time which items 677 are going to be created: 678 679 Begin("MyWindow"); 680 Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") 681 Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above 682 Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above 683 End(); 684 685 - If you want to completely hide the label, but still need an ID: 686 687 Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! 688 689 - Occasionally/rarely you might want change a label while preserving a constant ID. This allows 690 you to animate labels. For example you may want to include varying information in a window title bar, 691 but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: 692 693 Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID") 694 Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different 695 696 sprintf(buf, "My game (%f FPS)###MyGame", fps); 697 Begin(buf); // Variable title, ID = hash of "MyGame" 698 699 - Solving ID conflict in a more general manner: 700 Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts 701 within the same window. This is the most convenient way of distinguishing ID when iterating and 702 creating many UI elements programmatically. 703 You can push a pointer, a string or an integer value into the ID stack. 704 Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack. 705 At each level of the stack we store the seed used for items at this level of the ID stack. 706 707 Begin("Window"); 708 for (int i = 0; i < 100; i++) 709 { 710 PushID(i); // Push i to the id tack 711 Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click") 712 PopID(); 713 } 714 for (int i = 0; i < 100; i++) 715 { 716 MyObject* obj = Objects[i]; 717 PushID(obj); 718 Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click") 719 PopID(); 720 } 721 for (int i = 0; i < 100; i++) 722 { 723 MyObject* obj = Objects[i]; 724 PushID(obj->Name); 725 Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click") 726 PopID(); 727 } 728 End(); 729 730 - You can stack multiple prefixes into the ID stack: 731 732 Button("Click"); // Label = "Click", ID = hash of (..., "Click") 733 PushID("node"); 734 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") 735 PushID(my_ptr); 736 Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") 737 PopID(); 738 PopID(); 739 740 - Tree nodes implicitly creates a scope for you by calling PushID(). 741 742 Button("Click"); // Label = "Click", ID = hash of (..., "Click") 743 if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag) 744 { 745 Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") 746 TreePop(); 747 } 748 749 - When working with trees, ID are used to preserve the open/close state of each tree node. 750 Depending on your use cases you may want to use strings, indices or pointers as ID. 751 e.g. when following a single pointer that may change over time, using a static string as ID 752 will preserve your node open/closed state when the targeted object change. 753 e.g. when displaying a list of objects, using indices or pointers as ID will preserve the 754 node open/closed state differently. See what makes more sense in your situation! 755 756 Q: How can I display an image? What is ImTextureID, how does it works? 757 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples 758 759 Q: How can I use my own math types instead of ImVec2/ImVec4? 760 Q: How can I interact with standard C++ types (such as std::string and std::vector)? 761 Q: How can I display custom shapes? (using low-level ImDrawList API) 762 >> See https://www.dearimgui.org/faq 763 764 Q&A: Fonts, Text 765 ================ 766 767 Q: How can I load a different font than the default? 768 Q: How can I easily use icons in my application? 769 Q: How can I load multiple fonts? 770 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? 771 >> See https://www.dearimgui.org/faq and docs/FONTS.txt 772 773 Q&A: Concerns 774 ============= 775 776 Q: Who uses Dear ImGui? 777 Q: Can you create elaborate/serious tools with Dear ImGui? 778 Q: Can you reskin the look of Dear ImGui? 779 Q: Why using C++ (as opposed to C)? 780 >> See https://www.dearimgui.org/faq 781 782 Q&A: Community 783 ============== 784 785 Q: How can I help? 786 A: - Businesses: please reach out to "contact AT dearimgui.org" if you work in a place using Dear ImGui! 787 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. 788 This is among the most useful thing you can do for Dear ImGui. With increased funding we can hire more people working on this project. 789 - Individuals: you can support continued development via PayPal donations. See README. 790 - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt 791 and see how you want to help and can help! 792 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. 793 You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/3075). Visuals are ideal as they inspire other programmers. 794 But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. 795 - 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). 796 797*/ 798 799//------------------------------------------------------------------------- 800// [SECTION] INCLUDES 801//------------------------------------------------------------------------- 802 803#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 804#define _CRT_SECURE_NO_WARNINGS 805#endif 806 807#include "imgui.h" 808#ifndef IMGUI_DISABLE 809 810#ifndef IMGUI_DEFINE_MATH_OPERATORS 811#define IMGUI_DEFINE_MATH_OPERATORS 812#endif 813#include "imgui_internal.h" 814 815// System includes 816#include <ctype.h> // toupper 817#include <stdio.h> // vsnprintf, sscanf, printf 818#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier 819#include <stddef.h> // intptr_t 820#else 821#include <stdint.h> // intptr_t 822#endif 823 824// [Windows] OS specific includes (optional) 825#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) 826#define IMGUI_DISABLE_WIN32_FUNCTIONS 827#endif 828#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) 829#ifndef WIN32_LEAN_AND_MEAN 830#define WIN32_LEAN_AND_MEAN 831#endif 832#ifndef NOMINMAX 833#define NOMINMAX 834#endif 835#ifndef __MINGW32__ 836#include <Windows.h> // _wfopen, OpenClipboard 837#else 838#include <windows.h> 839#endif 840#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions 841#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS 842#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS 843#endif 844#endif 845 846// [Apple] OS specific includes 847#if defined(__APPLE__) 848#include <TargetConditionals.h> 849#endif 850 851// Visual Studio warnings 852#ifdef _MSC_VER 853#pragma warning (disable: 4127) // condition expression is constant 854#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 855#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later 856#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types 857#endif 858#endif 859 860// Clang/GCC warnings with -Weverything 861#if defined(__clang__) 862#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! 863#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. 864#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. 865#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. 866#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. 867#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. 868#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // 869#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. 870#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' 871#if __has_warning("-Wzero-as-null-pointer-constant") 872#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 873#endif 874#if __has_warning("-Wdouble-promotion") 875#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. 876#endif 877#elif defined(__GNUC__) 878// We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association. 879#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind 880#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used 881#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size 882#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' 883#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function 884#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value 885#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked 886#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false 887#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead 888#endif 889 890// Debug options 891#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL 892#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window 893#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) 894 895// 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. 896static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in 897static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear 898 899// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end) 900static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). 901static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. 902static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certaint time, unless mouse moved. 903 904//------------------------------------------------------------------------- 905// [SECTION] FORWARD DECLARATIONS 906//------------------------------------------------------------------------- 907 908static void SetCurrentWindow(ImGuiWindow* window); 909static void FindHoveredWindow(); 910static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); 911static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); 912 913static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list); 914static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window); 915 916static ImRect GetViewportRect(); 917 918// Settings 919static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); 920static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); 921static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf); 922 923// Platform Dependents default implementation for IO functions 924static const char* GetClipboardTextFn_DefaultImpl(void* user_data); 925static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); 926static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); 927 928namespace ImGui 929{ 930// Navigation 931static void NavUpdate(); 932static void NavUpdateWindowing(); 933static void NavUpdateWindowingOverlay(); 934static void NavUpdateMoveResult(); 935static float NavUpdatePageUpPageDown(); 936static inline void NavUpdateAnyRequestFlag(); 937static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand); 938static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); 939static ImVec2 NavCalcPreferredRefPos(); 940static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); 941static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); 942static int FindWindowFocusIndex(ImGuiWindow* window); 943 944// Error Checking 945static void ErrorCheckNewFrameSanityChecks(); 946static void ErrorCheckEndFrameSanityChecks(); 947static void ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write); 948 949// Misc 950static void UpdateSettings(); 951static void UpdateMouseInputs(); 952static void UpdateMouseWheel(); 953static void UpdateTabFocus(); 954static void UpdateDebugToolItemPicker(); 955static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); 956static void RenderWindowOuterBorders(ImGuiWindow* window); 957static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); 958static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); 959 960} 961 962//----------------------------------------------------------------------------- 963// [SECTION] CONTEXT AND MEMORY ALLOCATORS 964//----------------------------------------------------------------------------- 965 966// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. 967// ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). 968// 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call 969// SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading. 970// In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into. 971// 2) Important: Dear ImGui functions are not thread-safe because of this pointer. 972// If you want thread-safety to allow N threads to access N different contexts, you can: 973// - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h: 974// struct ImGuiContext; 975// extern thread_local ImGuiContext* MyImGuiTLS; 976// #define GImGui MyImGuiTLS 977// And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. 978// - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 979// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace. 980#ifndef GImGui 981ImGuiContext* GImGui = NULL; 982#endif 983 984// Memory Allocator functions. Use SetAllocatorFunctions() to change them. 985// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. 986// 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. 987#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS 988static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } 989static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } 990#else 991static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } 992static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } 993#endif 994 995static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper; 996static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper; 997static void* GImAllocatorUserData = NULL; 998 999//----------------------------------------------------------------------------- 1000// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) 1001//----------------------------------------------------------------------------- 1002 1003ImGuiStyle::ImGuiStyle() 1004{ 1005 Alpha = 1.0f; // Global alpha applies to everything in ImGui 1006 WindowPadding = ImVec2(8,8); // Padding within a window 1007 WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows 1008 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. 1009 WindowMinSize = ImVec2(32,32); // Minimum window size 1010 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text 1011 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. 1012 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows 1013 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. 1014 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows 1015 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. 1016 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) 1017 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). 1018 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. 1019 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines 1020 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) 1021 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! 1022 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). 1023 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). 1024 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar 1025 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar 1026 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar 1027 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. 1028 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. 1029 TabBorderSize = 0.0f; // Thickness of border around tabs. 1030 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. 1031 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. 1032 SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. 1033 DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. 1034 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. 1035 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. 1036 AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. 1037 AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) 1038 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. 1039 CircleSegmentMaxError = 1.60f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. 1040 1041 // Default theme 1042 ImGui::StyleColorsDark(this); 1043} 1044 1045// 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. 1046// 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. 1047void ImGuiStyle::ScaleAllSizes(float scale_factor) 1048{ 1049 WindowPadding = ImFloor(WindowPadding * scale_factor); 1050 WindowRounding = ImFloor(WindowRounding * scale_factor); 1051 WindowMinSize = ImFloor(WindowMinSize * scale_factor); 1052 ChildRounding = ImFloor(ChildRounding * scale_factor); 1053 PopupRounding = ImFloor(PopupRounding * scale_factor); 1054 FramePadding = ImFloor(FramePadding * scale_factor); 1055 FrameRounding = ImFloor(FrameRounding * scale_factor); 1056 ItemSpacing = ImFloor(ItemSpacing * scale_factor); 1057 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); 1058 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); 1059 IndentSpacing = ImFloor(IndentSpacing * scale_factor); 1060 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); 1061 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); 1062 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); 1063 GrabMinSize = ImFloor(GrabMinSize * scale_factor); 1064 GrabRounding = ImFloor(GrabRounding * scale_factor); 1065 TabRounding = ImFloor(TabRounding * scale_factor); 1066 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); 1067 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); 1068 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); 1069} 1070 1071ImGuiIO::ImGuiIO() 1072{ 1073 // Most fields are initialized with zero 1074 memset(this, 0, sizeof(*this)); 1075 IM_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT); // Our pre-C++11 IM_STATIC_ASSERT() macros triggers warning on modern compilers so we don't use it here. 1076 1077 // Settings 1078 ConfigFlags = ImGuiConfigFlags_None; 1079 BackendFlags = ImGuiBackendFlags_None; 1080 DisplaySize = ImVec2(-1.0f, -1.0f); 1081 DeltaTime = 1.0f/60.0f; 1082 IniSavingRate = 5.0f; 1083 IniFilename = "imgui.ini"; 1084 LogFilename = "imgui_log.txt"; 1085 MouseDoubleClickTime = 0.30f; 1086 MouseDoubleClickMaxDist = 6.0f; 1087 for (int i = 0; i < ImGuiKey_COUNT; i++) 1088 KeyMap[i] = -1; 1089 KeyRepeatDelay = 0.275f; 1090 KeyRepeatRate = 0.050f; 1091 UserData = NULL; 1092 1093 Fonts = NULL; 1094 FontGlobalScale = 1.0f; 1095 FontDefault = NULL; 1096 FontAllowUserScaling = false; 1097 DisplayFramebufferScale = ImVec2(1.0f, 1.0f); 1098 1099 // Miscellaneous options 1100 MouseDrawCursor = false; 1101#ifdef __APPLE__ 1102 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag 1103#else 1104 ConfigMacOSXBehaviors = false; 1105#endif 1106 ConfigInputTextCursorBlink = true; 1107 ConfigWindowsResizeFromEdges = true; 1108 ConfigWindowsMoveFromTitleBarOnly = false; 1109 ConfigWindowsMemoryCompactTimer = 60.0f; 1110 1111 // Platform Functions 1112 BackendPlatformName = BackendRendererName = NULL; 1113 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; 1114 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations 1115 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; 1116 ClipboardUserData = NULL; 1117 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; 1118 ImeWindowHandle = NULL; 1119 1120#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 1121 RenderDrawListsFn = NULL; 1122#endif 1123 1124 // Input (NB: we already have memset zero the entire structure!) 1125 MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 1126 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); 1127 MouseDragThreshold = 6.0f; 1128 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; 1129 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; 1130 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; 1131} 1132 1133// Pass in translated ASCII characters for text input. 1134// - with glfw you can get those from the callback set in glfwSetCharCallback() 1135// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message 1136void ImGuiIO::AddInputCharacter(unsigned int c) 1137{ 1138 InputQueueCharacters.push_back(c > 0 && c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); 1139} 1140 1141// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so 1142// we should save the high surrogate. 1143void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) 1144{ 1145 if ((c & 0xFC00) == 0xD800) // High surrogate, must save 1146 { 1147 if (InputQueueSurrogate != 0) 1148 InputQueueCharacters.push_back(0xFFFD); 1149 InputQueueSurrogate = c; 1150 return; 1151 } 1152 1153 ImWchar cp = c; 1154 if (InputQueueSurrogate != 0) 1155 { 1156 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate 1157 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); 1158 else if (IM_UNICODE_CODEPOINT_MAX == (0xFFFF)) // Codepoint will not fit in ImWchar (extra parenthesis around 0xFFFF somehow fixes -Wunreachable-code with Clang) 1159 cp = IM_UNICODE_CODEPOINT_INVALID; 1160 else 1161 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); 1162 InputQueueSurrogate = 0; 1163 } 1164 InputQueueCharacters.push_back(cp); 1165} 1166 1167void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) 1168{ 1169 while (*utf8_chars != 0) 1170 { 1171 unsigned int c = 0; 1172 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); 1173 if (c > 0) 1174 InputQueueCharacters.push_back((ImWchar)c); 1175 } 1176} 1177 1178void ImGuiIO::ClearInputCharacters() 1179{ 1180 InputQueueCharacters.resize(0); 1181} 1182 1183//----------------------------------------------------------------------------- 1184// [SECTION] MISC HELPERS/UTILITIES (Geometry functions) 1185//----------------------------------------------------------------------------- 1186 1187ImVec2 ImBezierClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) 1188{ 1189 IM_ASSERT(num_segments > 0); // Use ImBezierClosestPointCasteljau() 1190 ImVec2 p_last = p1; 1191 ImVec2 p_closest; 1192 float p_closest_dist2 = FLT_MAX; 1193 float t_step = 1.0f / (float)num_segments; 1194 for (int i_step = 1; i_step <= num_segments; i_step++) 1195 { 1196 ImVec2 p_current = ImBezierCalc(p1, p2, p3, p4, t_step * i_step); 1197 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); 1198 float dist2 = ImLengthSqr(p - p_line); 1199 if (dist2 < p_closest_dist2) 1200 { 1201 p_closest = p_line; 1202 p_closest_dist2 = dist2; 1203 } 1204 p_last = p_current; 1205 } 1206 return p_closest; 1207} 1208 1209// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp 1210static void BezierClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) 1211{ 1212 float dx = x4 - x1; 1213 float dy = y4 - y1; 1214 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); 1215 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); 1216 d2 = (d2 >= 0) ? d2 : -d2; 1217 d3 = (d3 >= 0) ? d3 : -d3; 1218 if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy)) 1219 { 1220 ImVec2 p_current(x4, y4); 1221 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); 1222 float dist2 = ImLengthSqr(p - p_line); 1223 if (dist2 < p_closest_dist2) 1224 { 1225 p_closest = p_line; 1226 p_closest_dist2 = dist2; 1227 } 1228 p_last = p_current; 1229 } 1230 else if (level < 10) 1231 { 1232 float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f; 1233 float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f; 1234 float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f; 1235 float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f; 1236 float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f; 1237 float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f; 1238 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); 1239 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); 1240 } 1241} 1242 1243// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol 1244// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. 1245ImVec2 ImBezierClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) 1246{ 1247 IM_ASSERT(tess_tol > 0.0f); 1248 ImVec2 p_last = p1; 1249 ImVec2 p_closest; 1250 float p_closest_dist2 = FLT_MAX; 1251 BezierClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); 1252 return p_closest; 1253} 1254 1255ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) 1256{ 1257 ImVec2 ap = p - a; 1258 ImVec2 ab_dir = b - a; 1259 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; 1260 if (dot < 0.0f) 1261 return a; 1262 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; 1263 if (dot > ab_len_sqr) 1264 return b; 1265 return a + ab_dir * dot / ab_len_sqr; 1266} 1267 1268bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) 1269{ 1270 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; 1271 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; 1272 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; 1273 return ((b1 == b2) && (b2 == b3)); 1274} 1275 1276void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) 1277{ 1278 ImVec2 v0 = b - a; 1279 ImVec2 v1 = c - a; 1280 ImVec2 v2 = p - a; 1281 const float denom = v0.x * v1.y - v1.x * v0.y; 1282 out_v = (v2.x * v1.y - v1.x * v2.y) / denom; 1283 out_w = (v0.x * v2.y - v2.x * v0.y) / denom; 1284 out_u = 1.0f - out_v - out_w; 1285} 1286 1287ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) 1288{ 1289 ImVec2 proj_ab = ImLineClosestPoint(a, b, p); 1290 ImVec2 proj_bc = ImLineClosestPoint(b, c, p); 1291 ImVec2 proj_ca = ImLineClosestPoint(c, a, p); 1292 float dist2_ab = ImLengthSqr(p - proj_ab); 1293 float dist2_bc = ImLengthSqr(p - proj_bc); 1294 float dist2_ca = ImLengthSqr(p - proj_ca); 1295 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); 1296 if (m == dist2_ab) 1297 return proj_ab; 1298 if (m == dist2_bc) 1299 return proj_bc; 1300 return proj_ca; 1301} 1302 1303//----------------------------------------------------------------------------- 1304// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) 1305//----------------------------------------------------------------------------- 1306 1307// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more. 1308int ImStricmp(const char* str1, const char* str2) 1309{ 1310 int d; 1311 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } 1312 return d; 1313} 1314 1315int ImStrnicmp(const char* str1, const char* str2, size_t count) 1316{ 1317 int d = 0; 1318 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } 1319 return d; 1320} 1321 1322void ImStrncpy(char* dst, const char* src, size_t count) 1323{ 1324 if (count < 1) 1325 return; 1326 if (count > 1) 1327 strncpy(dst, src, count - 1); 1328 dst[count - 1] = 0; 1329} 1330 1331char* ImStrdup(const char* str) 1332{ 1333 size_t len = strlen(str); 1334 void* buf = IM_ALLOC(len + 1); 1335 return (char*)memcpy(buf, (const void*)str, len + 1); 1336} 1337 1338char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) 1339{ 1340 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; 1341 size_t src_size = strlen(src) + 1; 1342 if (dst_buf_size < src_size) 1343 { 1344 IM_FREE(dst); 1345 dst = (char*)IM_ALLOC(src_size); 1346 if (p_dst_size) 1347 *p_dst_size = src_size; 1348 } 1349 return (char*)memcpy(dst, (const void*)src, src_size); 1350} 1351 1352const char* ImStrchrRange(const char* str, const char* str_end, char c) 1353{ 1354 const char* p = (const char*)memchr(str, (int)c, str_end - str); 1355 return p; 1356} 1357 1358int ImStrlenW(const ImWchar* str) 1359{ 1360 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit 1361 int n = 0; 1362 while (*str++) n++; 1363 return n; 1364} 1365 1366// Find end-of-line. Return pointer will point to either first \n, either str_end. 1367const char* ImStreolRange(const char* str, const char* str_end) 1368{ 1369 const char* p = (const char*)memchr(str, '\n', str_end - str); 1370 return p ? p : str_end; 1371} 1372 1373const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line 1374{ 1375 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') 1376 buf_mid_line--; 1377 return buf_mid_line; 1378} 1379 1380const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) 1381{ 1382 if (!needle_end) 1383 needle_end = needle + strlen(needle); 1384 1385 const char un0 = (char)toupper(*needle); 1386 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) 1387 { 1388 if (toupper(*haystack) == un0) 1389 { 1390 const char* b = needle + 1; 1391 for (const char* a = haystack + 1; b < needle_end; a++, b++) 1392 if (toupper(*a) != toupper(*b)) 1393 break; 1394 if (b == needle_end) 1395 return haystack; 1396 } 1397 haystack++; 1398 } 1399 return NULL; 1400} 1401 1402// 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. 1403void ImStrTrimBlanks(char* buf) 1404{ 1405 char* p = buf; 1406 while (p[0] == ' ' || p[0] == '\t') // Leading blanks 1407 p++; 1408 char* p_start = p; 1409 while (*p != 0) // Find end of string 1410 p++; 1411 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks 1412 p--; 1413 if (p_start != buf) // Copy memory if we had leading blanks 1414 memmove(buf, p_start, p - p_start); 1415 buf[p - p_start] = 0; // Zero terminate 1416} 1417 1418const char* ImStrSkipBlank(const char* str) 1419{ 1420 while (str[0] == ' ' || str[0] == '\t') 1421 str++; 1422 return str; 1423} 1424 1425// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). 1426// 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. 1427// B) When buf==NULL vsnprintf() will return the output size. 1428#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1429 1430// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h) 1431// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1432// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are 1433// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) 1434#ifdef IMGUI_USE_STB_SPRINTF 1435#define STB_SPRINTF_IMPLEMENTATION 1436#include "stb_sprintf.h" 1437#endif 1438 1439#if defined(_MSC_VER) && !defined(vsnprintf) 1440#define vsnprintf _vsnprintf 1441#endif 1442 1443int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) 1444{ 1445 va_list args; 1446 va_start(args, fmt); 1447#ifdef IMGUI_USE_STB_SPRINTF 1448 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); 1449#else 1450 int w = vsnprintf(buf, buf_size, fmt, args); 1451#endif 1452 va_end(args); 1453 if (buf == NULL) 1454 return w; 1455 if (w == -1 || w >= (int)buf_size) 1456 w = (int)buf_size - 1; 1457 buf[w] = 0; 1458 return w; 1459} 1460 1461int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) 1462{ 1463#ifdef IMGUI_USE_STB_SPRINTF 1464 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); 1465#else 1466 int w = vsnprintf(buf, buf_size, fmt, args); 1467#endif 1468 if (buf == NULL) 1469 return w; 1470 if (w == -1 || w >= (int)buf_size) 1471 w = (int)buf_size - 1; 1472 buf[w] = 0; 1473 return w; 1474} 1475#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1476 1477// CRC32 needs a 1KB lookup table (not cache friendly) 1478// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: 1479// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. 1480static const ImU32 GCrc32LookupTable[256] = 1481{ 1482 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, 1483 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, 1484 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, 1485 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, 1486 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, 1487 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, 1488 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, 1489 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, 1490 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, 1491 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, 1492 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, 1493 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, 1494 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, 1495 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, 1496 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, 1497 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, 1498}; 1499 1500// Known size hash 1501// It is ok to call ImHashData on a string with known length but the ### operator won't be supported. 1502// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. 1503ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed) 1504{ 1505 ImU32 crc = ~seed; 1506 const unsigned char* data = (const unsigned char*)data_p; 1507 const ImU32* crc32_lut = GCrc32LookupTable; 1508 while (data_size-- != 0) 1509 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; 1510 return ~crc; 1511} 1512 1513// Zero-terminated string hash, with support for ### to reset back to seed value 1514// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. 1515// Because this syntax is rarely used we are optimizing for the common case. 1516// - If we reach ### in the string we discard the hash so far and reset to the seed. 1517// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) 1518// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. 1519ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed) 1520{ 1521 seed = ~seed; 1522 ImU32 crc = seed; 1523 const unsigned char* data = (const unsigned char*)data_p; 1524 const ImU32* crc32_lut = GCrc32LookupTable; 1525 if (data_size != 0) 1526 { 1527 while (data_size-- != 0) 1528 { 1529 unsigned char c = *data++; 1530 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') 1531 crc = seed; 1532 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1533 } 1534 } 1535 else 1536 { 1537 while (unsigned char c = *data++) 1538 { 1539 if (c == '#' && data[0] == '#' && data[1] == '#') 1540 crc = seed; 1541 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1542 } 1543 } 1544 return ~crc; 1545} 1546 1547//----------------------------------------------------------------------------- 1548// [SECTION] MISC HELPERS/UTILITIES (File functions) 1549//----------------------------------------------------------------------------- 1550 1551// Default file functions 1552#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 1553 1554ImFileHandle ImFileOpen(const char* filename, const char* mode) 1555{ 1556#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__) 1557 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. 1558 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! 1559 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); 1560 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); 1561 ImVector<ImWchar> buf; 1562 buf.resize(filename_wsize + mode_wsize); 1563 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize); 1564 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize); 1565 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]); 1566#else 1567 return fopen(filename, mode); 1568#endif 1569} 1570 1571// We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way. 1572bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; } 1573ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; } 1574ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); } 1575ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); } 1576#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 1577 1578// Helper: Load file content into memory 1579// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree() 1580// This can't really be used with "rt" because fseek size won't match read size. 1581void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes) 1582{ 1583 IM_ASSERT(filename && mode); 1584 if (out_file_size) 1585 *out_file_size = 0; 1586 1587 ImFileHandle f; 1588 if ((f = ImFileOpen(filename, mode)) == NULL) 1589 return NULL; 1590 1591 size_t file_size = (size_t)ImFileGetSize(f); 1592 if (file_size == (size_t)-1) 1593 { 1594 ImFileClose(f); 1595 return NULL; 1596 } 1597 1598 void* file_data = IM_ALLOC(file_size + padding_bytes); 1599 if (file_data == NULL) 1600 { 1601 ImFileClose(f); 1602 return NULL; 1603 } 1604 if (ImFileRead(file_data, 1, file_size, f) != file_size) 1605 { 1606 ImFileClose(f); 1607 IM_FREE(file_data); 1608 return NULL; 1609 } 1610 if (padding_bytes > 0) 1611 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); 1612 1613 ImFileClose(f); 1614 if (out_file_size) 1615 *out_file_size = file_size; 1616 1617 return file_data; 1618} 1619 1620//----------------------------------------------------------------------------- 1621// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) 1622//----------------------------------------------------------------------------- 1623 1624// Convert UTF-8 to 32-bit character, process single character input. 1625// Based on stb_from_utf8() from github.com/nothings/stb/ 1626// We handle UTF-8 decoding error by skipping forward. 1627int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) 1628{ 1629 unsigned int c = (unsigned int)-1; 1630 const unsigned char* str = (const unsigned char*)in_text; 1631 if (!(*str & 0x80)) 1632 { 1633 c = (unsigned int)(*str++); 1634 *out_char = c; 1635 return 1; 1636 } 1637 if ((*str & 0xe0) == 0xc0) 1638 { 1639 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string 1640 if (in_text_end && in_text_end - (const char*)str < 2) return 1; 1641 if (*str < 0xc2) return 2; 1642 c = (unsigned int)((*str++ & 0x1f) << 6); 1643 if ((*str & 0xc0) != 0x80) return 2; 1644 c += (*str++ & 0x3f); 1645 *out_char = c; 1646 return 2; 1647 } 1648 if ((*str & 0xf0) == 0xe0) 1649 { 1650 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string 1651 if (in_text_end && in_text_end - (const char*)str < 3) return 1; 1652 if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; 1653 if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below 1654 c = (unsigned int)((*str++ & 0x0f) << 12); 1655 if ((*str & 0xc0) != 0x80) return 3; 1656 c += (unsigned int)((*str++ & 0x3f) << 6); 1657 if ((*str & 0xc0) != 0x80) return 3; 1658 c += (*str++ & 0x3f); 1659 *out_char = c; 1660 return 3; 1661 } 1662 if ((*str & 0xf8) == 0xf0) 1663 { 1664 *out_char = IM_UNICODE_CODEPOINT_INVALID; // will be invalid but not end of string 1665 if (in_text_end && in_text_end - (const char*)str < 4) return 1; 1666 if (*str > 0xf4) return 4; 1667 if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; 1668 if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below 1669 c = (unsigned int)((*str++ & 0x07) << 18); 1670 if ((*str & 0xc0) != 0x80) return 4; 1671 c += (unsigned int)((*str++ & 0x3f) << 12); 1672 if ((*str & 0xc0) != 0x80) return 4; 1673 c += (unsigned int)((*str++ & 0x3f) << 6); 1674 if ((*str & 0xc0) != 0x80) return 4; 1675 c += (*str++ & 0x3f); 1676 // utf-8 encodings of values used in surrogate pairs are invalid 1677 if ((c & 0xFFFFF800) == 0xD800) return 4; 1678 // If codepoint does not fit in ImWchar, use replacement character U+FFFD instead 1679 if (c > IM_UNICODE_CODEPOINT_MAX) c = IM_UNICODE_CODEPOINT_INVALID; 1680 *out_char = c; 1681 return 4; 1682 } 1683 *out_char = 0; 1684 return 0; 1685} 1686 1687int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) 1688{ 1689 ImWchar* buf_out = buf; 1690 ImWchar* buf_end = buf + buf_size; 1691 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) 1692 { 1693 unsigned int c; 1694 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1695 if (c == 0) 1696 break; 1697 *buf_out++ = (ImWchar)c; 1698 } 1699 *buf_out = 0; 1700 if (in_text_remaining) 1701 *in_text_remaining = in_text; 1702 return (int)(buf_out - buf); 1703} 1704 1705int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) 1706{ 1707 int char_count = 0; 1708 while ((!in_text_end || in_text < in_text_end) && *in_text) 1709 { 1710 unsigned int c; 1711 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1712 if (c == 0) 1713 break; 1714 char_count++; 1715 } 1716 return char_count; 1717} 1718 1719// Based on stb_to_utf8() from github.com/nothings/stb/ 1720static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) 1721{ 1722 if (c < 0x80) 1723 { 1724 buf[0] = (char)c; 1725 return 1; 1726 } 1727 if (c < 0x800) 1728 { 1729 if (buf_size < 2) return 0; 1730 buf[0] = (char)(0xc0 + (c >> 6)); 1731 buf[1] = (char)(0x80 + (c & 0x3f)); 1732 return 2; 1733 } 1734 if (c < 0x10000) 1735 { 1736 if (buf_size < 3) return 0; 1737 buf[0] = (char)(0xe0 + (c >> 12)); 1738 buf[1] = (char)(0x80 + ((c>> 6) & 0x3f)); 1739 buf[2] = (char)(0x80 + ((c ) & 0x3f)); 1740 return 3; 1741 } 1742 if (c <= 0x10FFFF) 1743 { 1744 if (buf_size < 4) return 0; 1745 buf[0] = (char)(0xf0 + (c >> 18)); 1746 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); 1747 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); 1748 buf[3] = (char)(0x80 + ((c ) & 0x3f)); 1749 return 4; 1750 } 1751 // Invalid code point, the max unicode is 0x10FFFF 1752 return 0; 1753} 1754 1755// Not optimal but we very rarely use this function. 1756int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) 1757{ 1758 unsigned int dummy = 0; 1759 return ImTextCharFromUtf8(&dummy, in_text, in_text_end); 1760} 1761 1762static inline int ImTextCountUtf8BytesFromChar(unsigned int c) 1763{ 1764 if (c < 0x80) return 1; 1765 if (c < 0x800) return 2; 1766 if (c < 0x10000) return 3; 1767 if (c <= 0x10FFFF) return 4; 1768 return 3; 1769} 1770 1771int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) 1772{ 1773 char* buf_out = buf; 1774 const char* buf_end = buf + buf_size; 1775 while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) 1776 { 1777 unsigned int c = (unsigned int)(*in_text++); 1778 if (c < 0x80) 1779 *buf_out++ = (char)c; 1780 else 1781 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c); 1782 } 1783 *buf_out = 0; 1784 return (int)(buf_out - buf); 1785} 1786 1787int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) 1788{ 1789 int bytes_count = 0; 1790 while ((!in_text_end || in_text < in_text_end) && *in_text) 1791 { 1792 unsigned int c = (unsigned int)(*in_text++); 1793 if (c < 0x80) 1794 bytes_count++; 1795 else 1796 bytes_count += ImTextCountUtf8BytesFromChar(c); 1797 } 1798 return bytes_count; 1799} 1800 1801//----------------------------------------------------------------------------- 1802// [SECTION] MISC HELPERS/UTILITIES (Color functions) 1803// Note: The Convert functions are early design which are not consistent with other API. 1804//----------------------------------------------------------------------------- 1805 1806IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) 1807{ 1808 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; 1809 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); 1810 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); 1811 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); 1812 return IM_COL32(r, g, b, 0xFF); 1813} 1814 1815ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) 1816{ 1817 float s = 1.0f/255.0f; 1818 return ImVec4( 1819 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, 1820 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, 1821 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, 1822 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); 1823} 1824 1825ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) 1826{ 1827 ImU32 out; 1828 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; 1829 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; 1830 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; 1831 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; 1832 return out; 1833} 1834 1835// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 1836// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv 1837void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) 1838{ 1839 float K = 0.f; 1840 if (g < b) 1841 { 1842 ImSwap(g, b); 1843 K = -1.f; 1844 } 1845 if (r < g) 1846 { 1847 ImSwap(r, g); 1848 K = -2.f / 6.f - K; 1849 } 1850 1851 const float chroma = r - (g < b ? g : b); 1852 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); 1853 out_s = chroma / (r + 1e-20f); 1854 out_v = r; 1855} 1856 1857// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 1858// also http://en.wikipedia.org/wiki/HSL_and_HSV 1859void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) 1860{ 1861 if (s == 0.0f) 1862 { 1863 // gray 1864 out_r = out_g = out_b = v; 1865 return; 1866 } 1867 1868 h = ImFmod(h, 1.0f) / (60.0f/360.0f); 1869 int i = (int)h; 1870 float f = h - (float)i; 1871 float p = v * (1.0f - s); 1872 float q = v * (1.0f - s * f); 1873 float t = v * (1.0f - s * (1.0f - f)); 1874 1875 switch (i) 1876 { 1877 case 0: out_r = v; out_g = t; out_b = p; break; 1878 case 1: out_r = q; out_g = v; out_b = p; break; 1879 case 2: out_r = p; out_g = v; out_b = t; break; 1880 case 3: out_r = p; out_g = q; out_b = v; break; 1881 case 4: out_r = t; out_g = p; out_b = v; break; 1882 case 5: default: out_r = v; out_g = p; out_b = q; break; 1883 } 1884} 1885 1886//----------------------------------------------------------------------------- 1887// [SECTION] ImGuiStorage 1888// Helper: Key->value storage 1889//----------------------------------------------------------------------------- 1890 1891// std::lower_bound but without the bullshit 1892static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key) 1893{ 1894 ImGuiStorage::ImGuiStoragePair* first = data.Data; 1895 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size; 1896 size_t count = (size_t)(last - first); 1897 while (count > 0) 1898 { 1899 size_t count2 = count >> 1; 1900 ImGuiStorage::ImGuiStoragePair* mid = first + count2; 1901 if (mid->key < key) 1902 { 1903 first = ++mid; 1904 count -= count2 + 1; 1905 } 1906 else 1907 { 1908 count = count2; 1909 } 1910 } 1911 return first; 1912} 1913 1914// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. 1915void ImGuiStorage::BuildSortByKey() 1916{ 1917 struct StaticFunc 1918 { 1919 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) 1920 { 1921 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. 1922 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; 1923 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1; 1924 return 0; 1925 } 1926 }; 1927 if (Data.Size > 1) 1928 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); 1929} 1930 1931int ImGuiStorage::GetInt(ImGuiID key, int default_val) const 1932{ 1933 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1934 if (it == Data.end() || it->key != key) 1935 return default_val; 1936 return it->val_i; 1937} 1938 1939bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const 1940{ 1941 return GetInt(key, default_val ? 1 : 0) != 0; 1942} 1943 1944float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const 1945{ 1946 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1947 if (it == Data.end() || it->key != key) 1948 return default_val; 1949 return it->val_f; 1950} 1951 1952void* ImGuiStorage::GetVoidPtr(ImGuiID key) const 1953{ 1954 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1955 if (it == Data.end() || it->key != key) 1956 return NULL; 1957 return it->val_p; 1958} 1959 1960// 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. 1961int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) 1962{ 1963 ImGuiStoragePair* it = LowerBound(Data, key); 1964 if (it == Data.end() || it->key != key) 1965 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1966 return &it->val_i; 1967} 1968 1969bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) 1970{ 1971 return (bool*)GetIntRef(key, default_val ? 1 : 0); 1972} 1973 1974float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) 1975{ 1976 ImGuiStoragePair* it = LowerBound(Data, key); 1977 if (it == Data.end() || it->key != key) 1978 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1979 return &it->val_f; 1980} 1981 1982void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) 1983{ 1984 ImGuiStoragePair* it = LowerBound(Data, key); 1985 if (it == Data.end() || it->key != key) 1986 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1987 return &it->val_p; 1988} 1989 1990// 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) 1991void ImGuiStorage::SetInt(ImGuiID key, int val) 1992{ 1993 ImGuiStoragePair* it = LowerBound(Data, key); 1994 if (it == Data.end() || it->key != key) 1995 { 1996 Data.insert(it, ImGuiStoragePair(key, val)); 1997 return; 1998 } 1999 it->val_i = val; 2000} 2001 2002void ImGuiStorage::SetBool(ImGuiID key, bool val) 2003{ 2004 SetInt(key, val ? 1 : 0); 2005} 2006 2007void ImGuiStorage::SetFloat(ImGuiID key, float val) 2008{ 2009 ImGuiStoragePair* it = LowerBound(Data, key); 2010 if (it == Data.end() || it->key != key) 2011 { 2012 Data.insert(it, ImGuiStoragePair(key, val)); 2013 return; 2014 } 2015 it->val_f = val; 2016} 2017 2018void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) 2019{ 2020 ImGuiStoragePair* it = LowerBound(Data, key); 2021 if (it == Data.end() || it->key != key) 2022 { 2023 Data.insert(it, ImGuiStoragePair(key, val)); 2024 return; 2025 } 2026 it->val_p = val; 2027} 2028 2029void ImGuiStorage::SetAllInt(int v) 2030{ 2031 for (int i = 0; i < Data.Size; i++) 2032 Data[i].val_i = v; 2033} 2034 2035//----------------------------------------------------------------------------- 2036// [SECTION] ImGuiTextFilter 2037//----------------------------------------------------------------------------- 2038 2039// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" 2040ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) 2041{ 2042 if (default_filter) 2043 { 2044 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); 2045 Build(); 2046 } 2047 else 2048 { 2049 InputBuf[0] = 0; 2050 CountGrep = 0; 2051 } 2052} 2053 2054bool ImGuiTextFilter::Draw(const char* label, float width) 2055{ 2056 if (width != 0.0f) 2057 ImGui::SetNextItemWidth(width); 2058 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); 2059 if (value_changed) 2060 Build(); 2061 return value_changed; 2062} 2063 2064void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const 2065{ 2066 out->resize(0); 2067 const char* wb = b; 2068 const char* we = wb; 2069 while (we < e) 2070 { 2071 if (*we == separator) 2072 { 2073 out->push_back(ImGuiTextRange(wb, we)); 2074 wb = we + 1; 2075 } 2076 we++; 2077 } 2078 if (wb != we) 2079 out->push_back(ImGuiTextRange(wb, we)); 2080} 2081 2082void ImGuiTextFilter::Build() 2083{ 2084 Filters.resize(0); 2085 ImGuiTextRange input_range(InputBuf, InputBuf+strlen(InputBuf)); 2086 input_range.split(',', &Filters); 2087 2088 CountGrep = 0; 2089 for (int i = 0; i != Filters.Size; i++) 2090 { 2091 ImGuiTextRange& f = Filters[i]; 2092 while (f.b < f.e && ImCharIsBlankA(f.b[0])) 2093 f.b++; 2094 while (f.e > f.b && ImCharIsBlankA(f.e[-1])) 2095 f.e--; 2096 if (f.empty()) 2097 continue; 2098 if (Filters[i].b[0] != '-') 2099 CountGrep += 1; 2100 } 2101} 2102 2103bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const 2104{ 2105 if (Filters.empty()) 2106 return true; 2107 2108 if (text == NULL) 2109 text = ""; 2110 2111 for (int i = 0; i != Filters.Size; i++) 2112 { 2113 const ImGuiTextRange& f = Filters[i]; 2114 if (f.empty()) 2115 continue; 2116 if (f.b[0] == '-') 2117 { 2118 // Subtract 2119 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL) 2120 return false; 2121 } 2122 else 2123 { 2124 // Grep 2125 if (ImStristr(text, text_end, f.b, f.e) != NULL) 2126 return true; 2127 } 2128 } 2129 2130 // Implicit * grep 2131 if (CountGrep == 0) 2132 return true; 2133 2134 return false; 2135} 2136 2137//----------------------------------------------------------------------------- 2138// [SECTION] ImGuiTextBuffer 2139//----------------------------------------------------------------------------- 2140 2141// On some platform vsnprintf() takes va_list by reference and modifies it. 2142// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. 2143#ifndef va_copy 2144#if defined(__GNUC__) || defined(__clang__) 2145#define va_copy(dest, src) __builtin_va_copy(dest, src) 2146#else 2147#define va_copy(dest, src) (dest = src) 2148#endif 2149#endif 2150 2151char ImGuiTextBuffer::EmptyString[1] = { 0 }; 2152 2153void ImGuiTextBuffer::append(const char* str, const char* str_end) 2154{ 2155 int len = str_end ? (int)(str_end - str) : (int)strlen(str); 2156 2157 // Add zero-terminator the first time 2158 const int write_off = (Buf.Size != 0) ? Buf.Size : 1; 2159 const int needed_sz = write_off + len; 2160 if (write_off + len >= Buf.Capacity) 2161 { 2162 int new_capacity = Buf.Capacity * 2; 2163 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); 2164 } 2165 2166 Buf.resize(needed_sz); 2167 memcpy(&Buf[write_off - 1], str, (size_t)len); 2168 Buf[write_off - 1 + len] = 0; 2169} 2170 2171void ImGuiTextBuffer::appendf(const char* fmt, ...) 2172{ 2173 va_list args; 2174 va_start(args, fmt); 2175 appendfv(fmt, args); 2176 va_end(args); 2177} 2178 2179// Helper: Text buffer for logging/accumulating text 2180void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) 2181{ 2182 va_list args_copy; 2183 va_copy(args_copy, args); 2184 2185 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. 2186 if (len <= 0) 2187 { 2188 va_end(args_copy); 2189 return; 2190 } 2191 2192 // Add zero-terminator the first time 2193 const int write_off = (Buf.Size != 0) ? Buf.Size : 1; 2194 const int needed_sz = write_off + len; 2195 if (write_off + len >= Buf.Capacity) 2196 { 2197 int new_capacity = Buf.Capacity * 2; 2198 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); 2199 } 2200 2201 Buf.resize(needed_sz); 2202 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); 2203 va_end(args_copy); 2204} 2205 2206//----------------------------------------------------------------------------- 2207// [SECTION] ImGuiListClipper 2208// This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed 2209// the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO) 2210//----------------------------------------------------------------------------- 2211 2212// Helper to calculate coarse clipping of large list of evenly sized items. 2213// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. 2214// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX 2215void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) 2216{ 2217 ImGuiContext& g = *GImGui; 2218 ImGuiWindow* window = g.CurrentWindow; 2219 if (g.LogEnabled) 2220 { 2221 // If logging is active, do not perform any clipping 2222 *out_items_display_start = 0; 2223 *out_items_display_end = items_count; 2224 return; 2225 } 2226 if (window->SkipItems) 2227 { 2228 *out_items_display_start = *out_items_display_end = 0; 2229 return; 2230 } 2231 2232 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect 2233 ImRect unclipped_rect = window->ClipRect; 2234 if (g.NavMoveRequest) 2235 unclipped_rect.Add(g.NavScoringRect); 2236 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) 2237 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); 2238 2239 const ImVec2 pos = window->DC.CursorPos; 2240 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); 2241 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); 2242 2243 // When performing a navigation request, ensure we have one item extra in the direction we are moving to 2244 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) 2245 start--; 2246 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) 2247 end++; 2248 2249 start = ImClamp(start, 0, items_count); 2250 end = ImClamp(end + 1, start, items_count); 2251 *out_items_display_start = start; 2252 *out_items_display_end = end; 2253} 2254 2255static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) 2256{ 2257 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. 2258 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. 2259 // The clipper should probably have a 4th step to display the last item in a regular manner. 2260 ImGuiContext& g = *GImGui; 2261 ImGuiWindow* window = g.CurrentWindow; 2262 window->DC.CursorPos.y = pos_y; 2263 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); 2264 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. 2265 window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. 2266 if (ImGuiColumns* columns = window->DC.CurrentColumns) 2267 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly 2268} 2269 2270// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 2271// Use case B: Begin() called from constructor with items_height>0 2272// 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. 2273void ImGuiListClipper::Begin(int count, float items_height) 2274{ 2275 ImGuiContext& g = *GImGui; 2276 ImGuiWindow* window = g.CurrentWindow; 2277 2278 StartPosY = window->DC.CursorPos.y; 2279 ItemsHeight = items_height; 2280 ItemsCount = count; 2281 StepNo = 0; 2282 DisplayEnd = DisplayStart = -1; 2283 if (ItemsHeight > 0.0f) 2284 { 2285 ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display 2286 if (DisplayStart > 0) 2287 SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor 2288 StepNo = 2; 2289 } 2290} 2291 2292void ImGuiListClipper::End() 2293{ 2294 if (ItemsCount < 0) 2295 return; 2296 // 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. 2297 if (ItemsCount < INT_MAX) 2298 SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor 2299 ItemsCount = -1; 2300 StepNo = 3; 2301} 2302 2303bool ImGuiListClipper::Step() 2304{ 2305 ImGuiContext& g = *GImGui; 2306 ImGuiWindow* window = g.CurrentWindow; 2307 2308 if (ItemsCount == 0 || window->SkipItems) 2309 { 2310 ItemsCount = -1; 2311 return false; 2312 } 2313 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. 2314 { 2315 DisplayStart = 0; 2316 DisplayEnd = 1; 2317 StartPosY = window->DC.CursorPos.y; 2318 StepNo = 1; 2319 return true; 2320 } 2321 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. 2322 { 2323 if (ItemsCount == 1) { ItemsCount = -1; return false; } 2324 float items_height = window->DC.CursorPos.y - StartPosY; 2325 IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically 2326 Begin(ItemsCount - 1, items_height); 2327 DisplayStart++; 2328 DisplayEnd++; 2329 StepNo = 3; 2330 return true; 2331 } 2332 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. 2333 { 2334 IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0); 2335 StepNo = 3; 2336 return true; 2337 } 2338 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. 2339 End(); 2340 return false; 2341} 2342 2343//----------------------------------------------------------------------------- 2344// [SECTION] STYLING 2345//----------------------------------------------------------------------------- 2346 2347ImGuiStyle& ImGui::GetStyle() 2348{ 2349 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 2350 return GImGui->Style; 2351} 2352 2353ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) 2354{ 2355 ImGuiStyle& style = GImGui->Style; 2356 ImVec4 c = style.Colors[idx]; 2357 c.w *= style.Alpha * alpha_mul; 2358 return ColorConvertFloat4ToU32(c); 2359} 2360 2361ImU32 ImGui::GetColorU32(const ImVec4& col) 2362{ 2363 ImGuiStyle& style = GImGui->Style; 2364 ImVec4 c = col; 2365 c.w *= style.Alpha; 2366 return ColorConvertFloat4ToU32(c); 2367} 2368 2369const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) 2370{ 2371 ImGuiStyle& style = GImGui->Style; 2372 return style.Colors[idx]; 2373} 2374 2375ImU32 ImGui::GetColorU32(ImU32 col) 2376{ 2377 ImGuiStyle& style = GImGui->Style; 2378 if (style.Alpha >= 1.0f) 2379 return col; 2380 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; 2381 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. 2382 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); 2383} 2384 2385// 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 2386void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) 2387{ 2388 ImGuiContext& g = *GImGui; 2389 ImGuiColorMod backup; 2390 backup.Col = idx; 2391 backup.BackupValue = g.Style.Colors[idx]; 2392 g.ColorModifiers.push_back(backup); 2393 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); 2394} 2395 2396void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) 2397{ 2398 ImGuiContext& g = *GImGui; 2399 ImGuiColorMod backup; 2400 backup.Col = idx; 2401 backup.BackupValue = g.Style.Colors[idx]; 2402 g.ColorModifiers.push_back(backup); 2403 g.Style.Colors[idx] = col; 2404} 2405 2406void ImGui::PopStyleColor(int count) 2407{ 2408 ImGuiContext& g = *GImGui; 2409 while (count > 0) 2410 { 2411 ImGuiColorMod& backup = g.ColorModifiers.back(); 2412 g.Style.Colors[backup.Col] = backup.BackupValue; 2413 g.ColorModifiers.pop_back(); 2414 count--; 2415 } 2416} 2417 2418struct ImGuiStyleVarInfo 2419{ 2420 ImGuiDataType Type; 2421 ImU32 Count; 2422 ImU32 Offset; 2423 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } 2424}; 2425 2426static const ImGuiStyleVarInfo GStyleVarInfo[] = 2427{ 2428 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha 2429 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding 2430 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding 2431 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize 2432 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize 2433 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign 2434 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding 2435 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize 2436 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding 2437 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize 2438 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding 2439 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding 2440 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize 2441 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing 2442 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing 2443 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing 2444 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize 2445 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding 2446 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize 2447 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding 2448 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding 2449 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign 2450 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign 2451}; 2452 2453static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) 2454{ 2455 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); 2456 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT); 2457 return &GStyleVarInfo[idx]; 2458} 2459 2460void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) 2461{ 2462 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 2463 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) 2464 { 2465 ImGuiContext& g = *GImGui; 2466 float* pvar = (float*)var_info->GetVarPtr(&g.Style); 2467 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); 2468 *pvar = val; 2469 return; 2470 } 2471 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); 2472} 2473 2474void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) 2475{ 2476 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 2477 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) 2478 { 2479 ImGuiContext& g = *GImGui; 2480 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); 2481 g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); 2482 *pvar = val; 2483 return; 2484 } 2485 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); 2486} 2487 2488void ImGui::PopStyleVar(int count) 2489{ 2490 ImGuiContext& g = *GImGui; 2491 while (count > 0) 2492 { 2493 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. 2494 ImGuiStyleMod& backup = g.StyleModifiers.back(); 2495 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); 2496 void* data = info->GetVarPtr(&g.Style); 2497 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } 2498 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } 2499 g.StyleModifiers.pop_back(); 2500 count--; 2501 } 2502} 2503 2504const char* ImGui::GetStyleColorName(ImGuiCol idx) 2505{ 2506 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; 2507 switch (idx) 2508 { 2509 case ImGuiCol_Text: return "Text"; 2510 case ImGuiCol_TextDisabled: return "TextDisabled"; 2511 case ImGuiCol_WindowBg: return "WindowBg"; 2512 case ImGuiCol_ChildBg: return "ChildBg"; 2513 case ImGuiCol_PopupBg: return "PopupBg"; 2514 case ImGuiCol_Border: return "Border"; 2515 case ImGuiCol_BorderShadow: return "BorderShadow"; 2516 case ImGuiCol_FrameBg: return "FrameBg"; 2517 case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; 2518 case ImGuiCol_FrameBgActive: return "FrameBgActive"; 2519 case ImGuiCol_TitleBg: return "TitleBg"; 2520 case ImGuiCol_TitleBgActive: return "TitleBgActive"; 2521 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; 2522 case ImGuiCol_MenuBarBg: return "MenuBarBg"; 2523 case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; 2524 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; 2525 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; 2526 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; 2527 case ImGuiCol_CheckMark: return "CheckMark"; 2528 case ImGuiCol_SliderGrab: return "SliderGrab"; 2529 case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; 2530 case ImGuiCol_Button: return "Button"; 2531 case ImGuiCol_ButtonHovered: return "ButtonHovered"; 2532 case ImGuiCol_ButtonActive: return "ButtonActive"; 2533 case ImGuiCol_Header: return "Header"; 2534 case ImGuiCol_HeaderHovered: return "HeaderHovered"; 2535 case ImGuiCol_HeaderActive: return "HeaderActive"; 2536 case ImGuiCol_Separator: return "Separator"; 2537 case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; 2538 case ImGuiCol_SeparatorActive: return "SeparatorActive"; 2539 case ImGuiCol_ResizeGrip: return "ResizeGrip"; 2540 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; 2541 case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; 2542 case ImGuiCol_Tab: return "Tab"; 2543 case ImGuiCol_TabHovered: return "TabHovered"; 2544 case ImGuiCol_TabActive: return "TabActive"; 2545 case ImGuiCol_TabUnfocused: return "TabUnfocused"; 2546 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; 2547 case ImGuiCol_PlotLines: return "PlotLines"; 2548 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; 2549 case ImGuiCol_PlotHistogram: return "PlotHistogram"; 2550 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; 2551 case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; 2552 case ImGuiCol_DragDropTarget: return "DragDropTarget"; 2553 case ImGuiCol_NavHighlight: return "NavHighlight"; 2554 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; 2555 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; 2556 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; 2557 } 2558 IM_ASSERT(0); 2559 return "Unknown"; 2560} 2561 2562//----------------------------------------------------------------------------- 2563// [SECTION] RENDER HELPERS 2564// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change, 2565// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context. 2566// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context. 2567//----------------------------------------------------------------------------- 2568 2569const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) 2570{ 2571 const char* text_display_end = text; 2572 if (!text_end) 2573 text_end = (const char*)-1; 2574 2575 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) 2576 text_display_end++; 2577 return text_display_end; 2578} 2579 2580// Internal ImGui functions to render text 2581// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() 2582void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) 2583{ 2584 ImGuiContext& g = *GImGui; 2585 ImGuiWindow* window = g.CurrentWindow; 2586 2587 // Hide anything after a '##' string 2588 const char* text_display_end; 2589 if (hide_text_after_hash) 2590 { 2591 text_display_end = FindRenderedTextEnd(text, text_end); 2592 } 2593 else 2594 { 2595 if (!text_end) 2596 text_end = text + strlen(text); // FIXME-OPT 2597 text_display_end = text_end; 2598 } 2599 2600 if (text != text_display_end) 2601 { 2602 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); 2603 if (g.LogEnabled) 2604 LogRenderedText(&pos, text, text_display_end); 2605 } 2606} 2607 2608void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) 2609{ 2610 ImGuiContext& g = *GImGui; 2611 ImGuiWindow* window = g.CurrentWindow; 2612 2613 if (!text_end) 2614 text_end = text + strlen(text); // FIXME-OPT 2615 2616 if (text != text_end) 2617 { 2618 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); 2619 if (g.LogEnabled) 2620 LogRenderedText(&pos, text, text_end); 2621 } 2622} 2623 2624// Default clip_rect uses (pos_min,pos_max) 2625// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) 2626void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) 2627{ 2628 // Perform CPU side clipping for single clipped element to avoid using scissor state 2629 ImVec2 pos = pos_min; 2630 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); 2631 2632 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; 2633 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; 2634 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); 2635 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min 2636 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); 2637 2638 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. 2639 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); 2640 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); 2641 2642 // Render 2643 if (need_clipping) 2644 { 2645 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); 2646 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); 2647 } 2648 else 2649 { 2650 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); 2651 } 2652} 2653 2654void 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) 2655{ 2656 // Hide anything after a '##' string 2657 const char* text_display_end = FindRenderedTextEnd(text, text_end); 2658 const int text_len = (int)(text_display_end - text); 2659 if (text_len == 0) 2660 return; 2661 2662 ImGuiContext& g = *GImGui; 2663 ImGuiWindow* window = g.CurrentWindow; 2664 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); 2665 if (g.LogEnabled) 2666 LogRenderedText(&pos_min, text, text_display_end); 2667} 2668 2669 2670// Another overly complex function until we reorganize everything into a nice all-in-one helper. 2671// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display. 2672// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. 2673void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) 2674{ 2675 ImGuiContext& g = *GImGui; 2676 if (text_end_full == NULL) 2677 text_end_full = FindRenderedTextEnd(text); 2678 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); 2679 2680 //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255)); 2681 //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255)); 2682 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255)); 2683 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. 2684 if (text_size.x > pos_max.x - pos_min.x) 2685 { 2686 // Hello wo... 2687 // | | | 2688 // min max ellipsis_max 2689 // <-> this is generally some padding value 2690 2691 const ImFont* font = draw_list->_Data->Font; 2692 const float font_size = draw_list->_Data->FontSize; 2693 const char* text_end_ellipsis = NULL; 2694 2695 ImWchar ellipsis_char = font->EllipsisChar; 2696 int ellipsis_char_count = 1; 2697 if (ellipsis_char == (ImWchar)-1) 2698 { 2699 ellipsis_char = (ImWchar)'.'; 2700 ellipsis_char_count = 3; 2701 } 2702 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); 2703 2704 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side 2705 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis 2706 2707 if (ellipsis_char_count > 1) 2708 { 2709 // Full ellipsis size without free spacing after it. 2710 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize); 2711 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots; 2712 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots; 2713 } 2714 2715 // We can now claim the space between pos_max.x and ellipsis_max.x 2716 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f); 2717 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; 2718 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full) 2719 { 2720 // Always display at least 1 character if there's no room for character + ellipsis 2721 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full); 2722 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x; 2723 } 2724 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) 2725 { 2726 // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text) 2727 text_end_ellipsis--; 2728 text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte 2729 } 2730 2731 // Render text, render ellipsis 2732 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); 2733 float ellipsis_x = pos_min.x + text_size_clipped_x; 2734 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x) 2735 for (int i = 0; i < ellipsis_char_count; i++) 2736 { 2737 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char); 2738 ellipsis_x += ellipsis_glyph_width; 2739 } 2740 } 2741 else 2742 { 2743 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); 2744 } 2745 2746 if (g.LogEnabled) 2747 LogRenderedText(&pos_min, text, text_end_full); 2748} 2749 2750// Render a rectangle shaped with optional rounding and borders 2751void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) 2752{ 2753 ImGuiContext& g = *GImGui; 2754 ImGuiWindow* window = g.CurrentWindow; 2755 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); 2756 const float border_size = g.Style.FrameBorderSize; 2757 if (border && border_size > 0.0f) 2758 { 2759 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); 2760 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 2761 } 2762} 2763 2764void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) 2765{ 2766 ImGuiContext& g = *GImGui; 2767 ImGuiWindow* window = g.CurrentWindow; 2768 const float border_size = g.Style.FrameBorderSize; 2769 if (border_size > 0.0f) 2770 { 2771 window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); 2772 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 2773 } 2774} 2775 2776void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) 2777{ 2778 ImGuiContext& g = *GImGui; 2779 if (id != g.NavId) 2780 return; 2781 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) 2782 return; 2783 ImGuiWindow* window = g.CurrentWindow; 2784 if (window->DC.NavHideHighlightOneFrame) 2785 return; 2786 2787 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; 2788 ImRect display_rect = bb; 2789 display_rect.ClipWith(window->ClipRect); 2790 if (flags & ImGuiNavHighlightFlags_TypeDefault) 2791 { 2792 const float THICKNESS = 2.0f; 2793 const float DISTANCE = 3.0f + THICKNESS * 0.5f; 2794 display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); 2795 bool fully_visible = window->ClipRect.Contains(display_rect); 2796 if (!fully_visible) 2797 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); 2798 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); 2799 if (!fully_visible) 2800 window->DrawList->PopClipRect(); 2801 } 2802 if (flags & ImGuiNavHighlightFlags_TypeThin) 2803 { 2804 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); 2805 } 2806} 2807 2808//----------------------------------------------------------------------------- 2809// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) 2810//----------------------------------------------------------------------------- 2811 2812// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods 2813ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) 2814 : DrawListInst(&context->DrawListSharedData) 2815{ 2816 Name = ImStrdup(name); 2817 ID = ImHashStr(name); 2818 IDStack.push_back(ID); 2819 Flags = ImGuiWindowFlags_None; 2820 Pos = ImVec2(0.0f, 0.0f); 2821 Size = SizeFull = ImVec2(0.0f, 0.0f); 2822 ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f); 2823 WindowPadding = ImVec2(0.0f, 0.0f); 2824 WindowRounding = 0.0f; 2825 WindowBorderSize = 0.0f; 2826 NameBufLen = (int)strlen(name) + 1; 2827 MoveId = GetID("#MOVE"); 2828 ChildId = 0; 2829 Scroll = ImVec2(0.0f, 0.0f); 2830 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 2831 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); 2832 ScrollbarSizes = ImVec2(0.0f, 0.0f); 2833 ScrollbarX = ScrollbarY = false; 2834 Active = WasActive = false; 2835 WriteAccessed = false; 2836 Collapsed = false; 2837 WantCollapseToggle = false; 2838 SkipItems = false; 2839 Appearing = false; 2840 Hidden = false; 2841 IsFallbackWindow = false; 2842 HasCloseButton = false; 2843 ResizeBorderHeld = -1; 2844 BeginCount = 0; 2845 BeginOrderWithinParent = -1; 2846 BeginOrderWithinContext = -1; 2847 PopupId = 0; 2848 AutoFitFramesX = AutoFitFramesY = -1; 2849 AutoFitChildAxises = 0x00; 2850 AutoFitOnlyGrows = false; 2851 AutoPosLastDirection = ImGuiDir_None; 2852 HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0; 2853 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; 2854 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); 2855 2856 InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used. 2857 2858 LastFrameActive = -1; 2859 LastTimeActive = -1.0f; 2860 ItemWidthDefault = 0.0f; 2861 FontWindowScale = 1.0f; 2862 SettingsOffset = -1; 2863 2864 DrawList = &DrawListInst; 2865 DrawList->_OwnerName = Name; 2866 ParentWindow = NULL; 2867 RootWindow = NULL; 2868 RootWindowForTitleBarHighlight = NULL; 2869 RootWindowForNav = NULL; 2870 2871 NavLastIds[0] = NavLastIds[1] = 0; 2872 NavRectRel[0] = NavRectRel[1] = ImRect(); 2873 NavLastChildNavWindow = NULL; 2874 2875 MemoryCompacted = false; 2876 MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0; 2877} 2878 2879ImGuiWindow::~ImGuiWindow() 2880{ 2881 IM_ASSERT(DrawList == &DrawListInst); 2882 IM_DELETE(Name); 2883 for (int i = 0; i != ColumnsStorage.Size; i++) 2884 ColumnsStorage[i].~ImGuiColumns(); 2885} 2886 2887ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) 2888{ 2889 ImGuiID seed = IDStack.back(); 2890 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 2891 ImGui::KeepAliveID(id); 2892 return id; 2893} 2894 2895ImGuiID ImGuiWindow::GetID(const void* ptr) 2896{ 2897 ImGuiID seed = IDStack.back(); 2898 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); 2899 ImGui::KeepAliveID(id); 2900 return id; 2901} 2902 2903ImGuiID ImGuiWindow::GetID(int n) 2904{ 2905 ImGuiID seed = IDStack.back(); 2906 ImGuiID id = ImHashData(&n, sizeof(n), seed); 2907 ImGui::KeepAliveID(id); 2908 return id; 2909} 2910 2911ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) 2912{ 2913 ImGuiID seed = IDStack.back(); 2914 return ImHashStr(str, str_end ? (str_end - str) : 0, seed); 2915} 2916 2917ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) 2918{ 2919 ImGuiID seed = IDStack.back(); 2920 return ImHashData(&ptr, sizeof(void*), seed); 2921} 2922 2923ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) 2924{ 2925 ImGuiID seed = IDStack.back(); 2926 return ImHashData(&n, sizeof(n), seed); 2927} 2928 2929// This is only used in rare/specific situations to manufacture an ID out of nowhere. 2930ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) 2931{ 2932 ImGuiID seed = IDStack.back(); 2933 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) }; 2934 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); 2935 ImGui::KeepAliveID(id); 2936 return id; 2937} 2938 2939static void SetCurrentWindow(ImGuiWindow* window) 2940{ 2941 ImGuiContext& g = *GImGui; 2942 g.CurrentWindow = window; 2943 if (window) 2944 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 2945} 2946 2947// Free up/compact internal window buffers, we can use this when a window becomes unused. 2948// This is currently unused by the library, but you may call this yourself for easy GC. 2949// Not freed: 2950// - ImGuiWindow, ImGuiWindowSettings, Name 2951// - StateStorage, ColumnsStorage (may hold useful data) 2952// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. 2953void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) 2954{ 2955 window->MemoryCompacted = true; 2956 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; 2957 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; 2958 window->IDStack.clear(); 2959 window->DrawList->ClearFreeMemory(); 2960 window->DC.ChildWindows.clear(); 2961 window->DC.ItemFlagsStack.clear(); 2962 window->DC.ItemWidthStack.clear(); 2963 window->DC.TextWrapPosStack.clear(); 2964 window->DC.GroupStack.clear(); 2965} 2966 2967void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) 2968{ 2969 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening. 2970 // The other buffers tends to amortize much faster. 2971 window->MemoryCompacted = false; 2972 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity); 2973 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity); 2974 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0; 2975} 2976 2977void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) 2978{ 2979 ImGuiContext& g = *GImGui; 2980 g.ActiveIdIsJustActivated = (g.ActiveId != id); 2981 if (g.ActiveIdIsJustActivated) 2982 { 2983 g.ActiveIdTimer = 0.0f; 2984 g.ActiveIdHasBeenPressedBefore = false; 2985 g.ActiveIdHasBeenEditedBefore = false; 2986 if (id != 0) 2987 { 2988 g.LastActiveId = id; 2989 g.LastActiveIdTimer = 0.0f; 2990 } 2991 } 2992 g.ActiveId = id; 2993 g.ActiveIdAllowOverlap = false; 2994 g.ActiveIdWindow = window; 2995 g.ActiveIdHasBeenEditedThisFrame = false; 2996 if (id) 2997 { 2998 g.ActiveIdIsAlive = id; 2999 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; 3000 } 3001 3002 // Clear declaration of inputs claimed by the widget 3003 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) 3004 g.ActiveIdUsingNavDirMask = 0x00; 3005 g.ActiveIdUsingNavInputMask = 0x00; 3006 g.ActiveIdUsingKeyInputMask = 0x00; 3007} 3008 3009void ImGui::ClearActiveID() 3010{ 3011 SetActiveID(0, NULL); 3012} 3013 3014void ImGui::SetHoveredID(ImGuiID id) 3015{ 3016 ImGuiContext& g = *GImGui; 3017 g.HoveredId = id; 3018 g.HoveredIdAllowOverlap = false; 3019 if (id != 0 && g.HoveredIdPreviousFrame != id) 3020 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; 3021} 3022 3023ImGuiID ImGui::GetHoveredID() 3024{ 3025 ImGuiContext& g = *GImGui; 3026 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; 3027} 3028 3029void ImGui::KeepAliveID(ImGuiID id) 3030{ 3031 ImGuiContext& g = *GImGui; 3032 if (g.ActiveId == id) 3033 g.ActiveIdIsAlive = id; 3034 if (g.ActiveIdPreviousFrame == id) 3035 g.ActiveIdPreviousFrameIsAlive = true; 3036} 3037 3038void ImGui::MarkItemEdited(ImGuiID id) 3039{ 3040 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). 3041 // 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. 3042 ImGuiContext& g = *GImGui; 3043 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); 3044 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. 3045 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); 3046 g.ActiveIdHasBeenEditedThisFrame = true; 3047 g.ActiveIdHasBeenEditedBefore = true; 3048 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; 3049} 3050 3051static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) 3052{ 3053 // An active popup disable hovering on other windows (apart from its own children) 3054 // FIXME-OPT: This could be cached/stored within the window. 3055 ImGuiContext& g = *GImGui; 3056 if (g.NavWindow) 3057 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) 3058 if (focused_root_window->WasActive && focused_root_window != window->RootWindow) 3059 { 3060 // For the purpose of those flags we differentiate "standard popup" from "modal popup" 3061 // NB: The order of those two tests is important because Modal windows are also Popups. 3062 if (focused_root_window->Flags & ImGuiWindowFlags_Modal) 3063 return false; 3064 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 3065 return false; 3066 } 3067 return true; 3068} 3069 3070// This is roughly matching the behavior of internal-facing ItemHoverable() 3071// - 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() 3072// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId 3073bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) 3074{ 3075 ImGuiContext& g = *GImGui; 3076 ImGuiWindow* window = g.CurrentWindow; 3077 if (g.NavDisableMouseHover && !g.NavDisableHighlight) 3078 return IsItemFocused(); 3079 3080 // Test for bounding box overlap, as updated as ItemAdd() 3081 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 3082 return false; 3083 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function 3084 3085 // Test if we are hovering the right window (our window could be behind another window) 3086 // [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. 3087 // 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. 3088 //if (g.HoveredWindow != window) 3089 // return false; 3090 if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped)) 3091 return false; 3092 3093 // Test if another item is active (e.g. being dragged) 3094 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 3095 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) 3096 return false; 3097 3098 // Test if interactions on this window are blocked by an active popup or modal. 3099 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. 3100 if (!IsWindowContentHoverable(window, flags)) 3101 return false; 3102 3103 // Test if the item is disabled 3104 if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) 3105 return false; 3106 3107 // Special handling for the dummy item after Begin() which represent the title bar or tab. 3108 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. 3109 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) 3110 return false; 3111 return true; 3112} 3113 3114// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). 3115bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) 3116{ 3117 ImGuiContext& g = *GImGui; 3118 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) 3119 return false; 3120 3121 ImGuiWindow* window = g.CurrentWindow; 3122 if (g.HoveredWindow != window) 3123 return false; 3124 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) 3125 return false; 3126 if (!IsMouseHoveringRect(bb.Min, bb.Max)) 3127 return false; 3128 if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) 3129 return false; 3130 if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) 3131 return false; 3132 3133 SetHoveredID(id); 3134 3135 // [DEBUG] Item Picker tool! 3136 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making 3137 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered 3138 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost. 3139 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd(). 3140 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) 3141 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); 3142 if (g.DebugItemPickerBreakId == id) 3143 IM_DEBUG_BREAK(); 3144 3145 return true; 3146} 3147 3148bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) 3149{ 3150 ImGuiContext& g = *GImGui; 3151 ImGuiWindow* window = g.CurrentWindow; 3152 if (!bb.Overlaps(window->ClipRect)) 3153 if (id == 0 || (id != g.ActiveId && id != g.NavId)) 3154 if (clip_even_when_logged || !g.LogEnabled) 3155 return true; 3156 return false; 3157} 3158 3159// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. 3160bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) 3161{ 3162 ImGuiContext& g = *GImGui; 3163 3164 // Increment counters 3165 const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; 3166 window->DC.FocusCounterRegular++; 3167 if (is_tab_stop) 3168 window->DC.FocusCounterTabStop++; 3169 3170 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. 3171 // (Note that we can always TAB out of a widget that doesn't allow tabbing in) 3172 if (g.ActiveId == id && g.FocusTabPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.FocusRequestNextWindow == NULL) 3173 { 3174 g.FocusRequestNextWindow = window; 3175 g.FocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. 3176 } 3177 3178 // Handle focus requests 3179 if (g.FocusRequestCurrWindow == window) 3180 { 3181 if (window->DC.FocusCounterRegular == g.FocusRequestCurrCounterRegular) 3182 return true; 3183 if (is_tab_stop && window->DC.FocusCounterTabStop == g.FocusRequestCurrCounterTabStop) 3184 { 3185 g.NavJustTabbedId = id; 3186 return true; 3187 } 3188 3189 // If another item is about to be focused, we clear our own active id 3190 if (g.ActiveId == id) 3191 ClearActiveID(); 3192 } 3193 3194 return false; 3195} 3196 3197void ImGui::FocusableItemUnregister(ImGuiWindow* window) 3198{ 3199 window->DC.FocusCounterRegular--; 3200 window->DC.FocusCounterTabStop--; 3201} 3202 3203float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) 3204{ 3205 if (wrap_pos_x < 0.0f) 3206 return 0.0f; 3207 3208 ImGuiWindow* window = GImGui->CurrentWindow; 3209 if (wrap_pos_x == 0.0f) 3210 wrap_pos_x = window->WorkRect.Max.x; 3211 else if (wrap_pos_x > 0.0f) 3212 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space 3213 3214 return ImMax(wrap_pos_x - pos.x, 1.0f); 3215} 3216 3217// IM_ALLOC() == ImGui::MemAlloc() 3218void* ImGui::MemAlloc(size_t size) 3219{ 3220 if (ImGuiContext* ctx = GImGui) 3221 ctx->IO.MetricsActiveAllocations++; 3222 return GImAllocatorAllocFunc(size, GImAllocatorUserData); 3223} 3224 3225// IM_FREE() == ImGui::MemFree() 3226void ImGui::MemFree(void* ptr) 3227{ 3228 if (ptr) 3229 if (ImGuiContext* ctx = GImGui) 3230 ctx->IO.MetricsActiveAllocations--; 3231 return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); 3232} 3233 3234const char* ImGui::GetClipboardText() 3235{ 3236 ImGuiContext& g = *GImGui; 3237 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : ""; 3238} 3239 3240void ImGui::SetClipboardText(const char* text) 3241{ 3242 ImGuiContext& g = *GImGui; 3243 if (g.IO.SetClipboardTextFn) 3244 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text); 3245} 3246 3247const char* ImGui::GetVersion() 3248{ 3249 return IMGUI_VERSION; 3250} 3251 3252// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself 3253// 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 3254ImGuiContext* ImGui::GetCurrentContext() 3255{ 3256 return GImGui; 3257} 3258 3259void ImGui::SetCurrentContext(ImGuiContext* ctx) 3260{ 3261#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC 3262 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. 3263#else 3264 GImGui = ctx; 3265#endif 3266} 3267 3268void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) 3269{ 3270 GImAllocatorAllocFunc = alloc_func; 3271 GImAllocatorFreeFunc = free_func; 3272 GImAllocatorUserData = user_data; 3273} 3274 3275ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) 3276{ 3277 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); 3278 if (GImGui == NULL) 3279 SetCurrentContext(ctx); 3280 Initialize(ctx); 3281 return ctx; 3282} 3283 3284void ImGui::DestroyContext(ImGuiContext* ctx) 3285{ 3286 if (ctx == NULL) 3287 ctx = GImGui; 3288 Shutdown(ctx); 3289 if (GImGui == ctx) 3290 SetCurrentContext(NULL); 3291 IM_DELETE(ctx); 3292} 3293 3294ImGuiIO& ImGui::GetIO() 3295{ 3296 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 3297 return GImGui->IO; 3298} 3299 3300// Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame() 3301ImDrawData* ImGui::GetDrawData() 3302{ 3303 ImGuiContext& g = *GImGui; 3304 return g.DrawData.Valid ? &g.DrawData : NULL; 3305} 3306 3307double ImGui::GetTime() 3308{ 3309 return GImGui->Time; 3310} 3311 3312int ImGui::GetFrameCount() 3313{ 3314 return GImGui->FrameCount; 3315} 3316 3317ImDrawList* ImGui::GetBackgroundDrawList() 3318{ 3319 return &GImGui->BackgroundDrawList; 3320} 3321 3322ImDrawList* ImGui::GetForegroundDrawList() 3323{ 3324 return &GImGui->ForegroundDrawList; 3325} 3326 3327ImDrawListSharedData* ImGui::GetDrawListSharedData() 3328{ 3329 return &GImGui->DrawListSharedData; 3330} 3331 3332void ImGui::StartMouseMovingWindow(ImGuiWindow* window) 3333{ 3334 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. 3335 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. 3336 // This is because we want ActiveId to be set even when the window is not permitted to move. 3337 ImGuiContext& g = *GImGui; 3338 FocusWindow(window); 3339 SetActiveID(window->MoveId, window); 3340 g.NavDisableHighlight = true; 3341 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; 3342 3343 bool can_move_window = true; 3344 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) 3345 can_move_window = false; 3346 if (can_move_window) 3347 g.MovingWindow = window; 3348} 3349 3350// Handle mouse moving window 3351// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() 3352// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId. 3353// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs, 3354// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other. 3355void ImGui::UpdateMouseMovingWindowNewFrame() 3356{ 3357 ImGuiContext& g = *GImGui; 3358 if (g.MovingWindow != NULL) 3359 { 3360 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). 3361 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. 3362 KeepAliveID(g.ActiveId); 3363 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); 3364 ImGuiWindow* moving_window = g.MovingWindow->RootWindow; 3365 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) 3366 { 3367 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; 3368 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) 3369 { 3370 MarkIniSettingsDirty(moving_window); 3371 SetWindowPos(moving_window, pos, ImGuiCond_Always); 3372 } 3373 FocusWindow(g.MovingWindow); 3374 } 3375 else 3376 { 3377 ClearActiveID(); 3378 g.MovingWindow = NULL; 3379 } 3380 } 3381 else 3382 { 3383 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. 3384 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) 3385 { 3386 KeepAliveID(g.ActiveId); 3387 if (!g.IO.MouseDown[0]) 3388 ClearActiveID(); 3389 } 3390 } 3391} 3392 3393// Initiate moving window when clicking on empty space or title bar. 3394// Handle left-click and right-click focus. 3395void ImGui::UpdateMouseMovingWindowEndFrame() 3396{ 3397 ImGuiContext& g = *GImGui; 3398 if (g.ActiveId != 0 || g.HoveredId != 0) 3399 return; 3400 3401 // Unless we just made a window/popup appear 3402 if (g.NavWindow && g.NavWindow->Appearing) 3403 return; 3404 3405 // Click to focus window and start moving (after we're done with all our widgets) 3406 if (g.IO.MouseClicked[0]) 3407 { 3408 if (g.HoveredRootWindow != NULL) 3409 { 3410 StartMouseMovingWindow(g.HoveredWindow); 3411 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) 3412 if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) 3413 g.MovingWindow = NULL; 3414 } 3415 else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL) 3416 { 3417 // Clicking on void disable focus 3418 FocusWindow(NULL); 3419 } 3420 } 3421 3422 // With right mouse button we close popups without changing focus based on where the mouse is aimed 3423 // Instead, focus will be restored to the window under the bottom-most closed popup. 3424 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) 3425 if (g.IO.MouseClicked[1]) 3426 { 3427 // Find the top-most window between HoveredWindow and the top-most Modal Window. 3428 // This is where we can trim the popup stack. 3429 ImGuiWindow* modal = GetTopMostPopupModal(); 3430 bool hovered_window_above_modal = false; 3431 if (modal == NULL) 3432 hovered_window_above_modal = true; 3433 for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) 3434 { 3435 ImGuiWindow* window = g.Windows[i]; 3436 if (window == modal) 3437 break; 3438 if (window == g.HoveredWindow) 3439 hovered_window_above_modal = true; 3440 } 3441 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); 3442 } 3443} 3444 3445static bool IsWindowActiveAndVisible(ImGuiWindow* window) 3446{ 3447 return (window->Active) && (!window->Hidden); 3448} 3449 3450static void ImGui::UpdateMouseInputs() 3451{ 3452 ImGuiContext& g = *GImGui; 3453 3454 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) 3455 if (IsMousePosValid(&g.IO.MousePos)) 3456 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); 3457 3458 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta 3459 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) 3460 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; 3461 else 3462 g.IO.MouseDelta = ImVec2(0.0f, 0.0f); 3463 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) 3464 g.NavDisableMouseHover = false; 3465 3466 g.IO.MousePosPrev = g.IO.MousePos; 3467 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3468 { 3469 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; 3470 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; 3471 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; 3472 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; 3473 g.IO.MouseDoubleClicked[i] = false; 3474 if (g.IO.MouseClicked[i]) 3475 { 3476 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) 3477 { 3478 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); 3479 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) 3480 g.IO.MouseDoubleClicked[i] = true; 3481 g.IO.MouseClickedTime[i] = -DBL_MAX; // so the third click isn't turned into a double-click 3482 } 3483 else 3484 { 3485 g.IO.MouseClickedTime[i] = g.Time; 3486 } 3487 g.IO.MouseClickedPos[i] = g.IO.MousePos; 3488 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; 3489 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); 3490 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; 3491 } 3492 else if (g.IO.MouseDown[i]) 3493 { 3494 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold 3495 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); 3496 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); 3497 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); 3498 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); 3499 } 3500 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) 3501 g.IO.MouseDownWasDoubleClick[i] = false; 3502 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation 3503 g.NavDisableMouseHover = false; 3504 } 3505} 3506 3507static void StartLockWheelingWindow(ImGuiWindow* window) 3508{ 3509 ImGuiContext& g = *GImGui; 3510 if (g.WheelingWindow == window) 3511 return; 3512 g.WheelingWindow = window; 3513 g.WheelingWindowRefMousePos = g.IO.MousePos; 3514 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; 3515} 3516 3517void ImGui::UpdateMouseWheel() 3518{ 3519 ImGuiContext& g = *GImGui; 3520 3521 // Reset the locked window if we move the mouse or after the timer elapses 3522 if (g.WheelingWindow != NULL) 3523 { 3524 g.WheelingWindowTimer -= g.IO.DeltaTime; 3525 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) 3526 g.WheelingWindowTimer = 0.0f; 3527 if (g.WheelingWindowTimer <= 0.0f) 3528 { 3529 g.WheelingWindow = NULL; 3530 g.WheelingWindowTimer = 0.0f; 3531 } 3532 } 3533 3534 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f) 3535 return; 3536 3537 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; 3538 if (!window || window->Collapsed) 3539 return; 3540 3541 // Zoom / Scale window 3542 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. 3543 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) 3544 { 3545 StartLockWheelingWindow(window); 3546 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); 3547 const float scale = new_font_scale / window->FontWindowScale; 3548 window->FontWindowScale = new_font_scale; 3549 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) 3550 { 3551 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; 3552 SetWindowPos(window, window->Pos + offset, 0); 3553 window->Size = ImFloor(window->Size * scale); 3554 window->SizeFull = ImFloor(window->SizeFull * scale); 3555 } 3556 return; 3557 } 3558 3559 // Mouse wheel scrolling 3560 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent 3561 3562 // Vertical Mouse Wheel scrolling 3563 const float wheel_y = (g.IO.MouseWheel != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; 3564 if (wheel_y != 0.0f && !g.IO.KeyCtrl) 3565 { 3566 StartLockWheelingWindow(window); 3567 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) 3568 window = window->ParentWindow; 3569 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) 3570 { 3571 float max_step = window->InnerRect.GetHeight() * 0.67f; 3572 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); 3573 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step); 3574 } 3575 } 3576 3577 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held 3578 const float wheel_x = (g.IO.MouseWheelH != 0.0f && !g.IO.KeyShift) ? g.IO.MouseWheelH : (g.IO.MouseWheel != 0.0f && g.IO.KeyShift) ? g.IO.MouseWheel : 0.0f; 3579 if (wheel_x != 0.0f && !g.IO.KeyCtrl) 3580 { 3581 StartLockWheelingWindow(window); 3582 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) 3583 window = window->ParentWindow; 3584 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) 3585 { 3586 float max_step = window->InnerRect.GetWidth() * 0.67f; 3587 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); 3588 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step); 3589 } 3590 } 3591} 3592 3593void ImGui::UpdateTabFocus() 3594{ 3595 ImGuiContext& g = *GImGui; 3596 3597 // Pressing TAB activate widget focus 3598 g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); 3599 if (g.ActiveId == 0 && g.FocusTabPressed) 3600 { 3601 // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also 3602 // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. 3603 g.FocusRequestNextWindow = g.NavWindow; 3604 g.FocusRequestNextCounterRegular = INT_MAX; 3605 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) 3606 g.FocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); 3607 else 3608 g.FocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; 3609 } 3610 3611 // Turn queued focus request into current one 3612 g.FocusRequestCurrWindow = NULL; 3613 g.FocusRequestCurrCounterRegular = g.FocusRequestCurrCounterTabStop = INT_MAX; 3614 if (g.FocusRequestNextWindow != NULL) 3615 { 3616 ImGuiWindow* window = g.FocusRequestNextWindow; 3617 g.FocusRequestCurrWindow = window; 3618 if (g.FocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) 3619 g.FocusRequestCurrCounterRegular = ImModPositive(g.FocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); 3620 if (g.FocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) 3621 g.FocusRequestCurrCounterTabStop = ImModPositive(g.FocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); 3622 g.FocusRequestNextWindow = NULL; 3623 g.FocusRequestNextCounterRegular = g.FocusRequestNextCounterTabStop = INT_MAX; 3624 } 3625 3626 g.NavIdTabCounter = INT_MAX; 3627} 3628 3629// 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) 3630void ImGui::UpdateHoveredWindowAndCaptureFlags() 3631{ 3632 ImGuiContext& g = *GImGui; 3633 3634 // Find the window hovered by mouse: 3635 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. 3636 // - 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. 3637 // - 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. 3638 FindHoveredWindow(); 3639 3640 // Modal windows prevents cursor from hovering behind them. 3641 ImGuiWindow* modal_window = GetTopMostPopupModal(); 3642 if (modal_window) 3643 if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) 3644 g.HoveredRootWindow = g.HoveredWindow = NULL; 3645 3646 // Disabled mouse? 3647 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) 3648 g.HoveredWindow = g.HoveredRootWindow = NULL; 3649 3650 // 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. 3651 int mouse_earliest_button_down = -1; 3652 bool mouse_any_down = false; 3653 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3654 { 3655 if (g.IO.MouseClicked[i]) 3656 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty()); 3657 mouse_any_down |= g.IO.MouseDown[i]; 3658 if (g.IO.MouseDown[i]) 3659 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) 3660 mouse_earliest_button_down = i; 3661 } 3662 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; 3663 3664 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. 3665 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) 3666 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; 3667 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) 3668 g.HoveredWindow = g.HoveredRootWindow = NULL; 3669 3670 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) 3671 if (g.WantCaptureMouseNextFrame != -1) 3672 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); 3673 else 3674 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty()); 3675 3676 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) 3677 if (g.WantCaptureKeyboardNextFrame != -1) 3678 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); 3679 else 3680 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); 3681 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) 3682 g.IO.WantCaptureKeyboard = true; 3683 3684 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible 3685 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; 3686} 3687 3688ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() 3689{ 3690 ImGuiContext& g = *GImGui; 3691 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None; 3692 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; } 3693 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; } 3694 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; } 3695 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; } 3696 return key_mod_flags; 3697} 3698 3699void ImGui::NewFrame() 3700{ 3701 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 3702 ImGuiContext& g = *GImGui; 3703 3704#ifdef IMGUI_ENABLE_TEST_ENGINE 3705 ImGuiTestEngineHook_PreNewFrame(&g); 3706#endif 3707 3708 // Check and assert for various common IO and Configuration mistakes 3709 ErrorCheckNewFrameSanityChecks(); 3710 3711 // Load settings on first frame, save settings when modified (after a delay) 3712 UpdateSettings(); 3713 3714 g.Time += g.IO.DeltaTime; 3715 g.WithinFrameScope = true; 3716 g.FrameCount += 1; 3717 g.TooltipOverrideCount = 0; 3718 g.WindowsActiveCount = 0; 3719 g.MenusIdSubmittedThisFrame.resize(0); 3720 3721 // Calculate frame-rate for the user, as a purely luxurious feature 3722 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; 3723 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; 3724 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); 3725 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; 3726 3727 // Setup current font and draw list shared data 3728 g.IO.Fonts->Locked = true; 3729 SetCurrentFont(GetDefaultFont()); 3730 IM_ASSERT(g.Font->IsLoaded()); 3731 g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); 3732 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; 3733 g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); 3734 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; 3735 if (g.Style.AntiAliasedLines) 3736 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; 3737 if (g.Style.AntiAliasedFill) 3738 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; 3739 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) 3740 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; 3741 3742 g.BackgroundDrawList.Clear(); 3743 g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); 3744 g.BackgroundDrawList.PushClipRectFullScreen(); 3745 3746 g.ForegroundDrawList.Clear(); 3747 g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); 3748 g.ForegroundDrawList.PushClipRectFullScreen(); 3749 3750 // Mark rendering data as invalid to prevent user who may have a handle on it to use it. 3751 g.DrawData.Clear(); 3752 3753 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent 3754 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) 3755 KeepAliveID(g.DragDropPayload.SourceId); 3756 3757 // Update HoveredId data 3758 if (!g.HoveredIdPreviousFrame) 3759 g.HoveredIdTimer = 0.0f; 3760 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) 3761 g.HoveredIdNotActiveTimer = 0.0f; 3762 if (g.HoveredId) 3763 g.HoveredIdTimer += g.IO.DeltaTime; 3764 if (g.HoveredId && g.ActiveId != g.HoveredId) 3765 g.HoveredIdNotActiveTimer += g.IO.DeltaTime; 3766 g.HoveredIdPreviousFrame = g.HoveredId; 3767 g.HoveredId = 0; 3768 g.HoveredIdAllowOverlap = false; 3769 3770 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) 3771 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) 3772 ClearActiveID(); 3773 if (g.ActiveId) 3774 g.ActiveIdTimer += g.IO.DeltaTime; 3775 g.LastActiveIdTimer += g.IO.DeltaTime; 3776 g.ActiveIdPreviousFrame = g.ActiveId; 3777 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow; 3778 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore; 3779 g.ActiveIdIsAlive = 0; 3780 g.ActiveIdHasBeenEditedThisFrame = false; 3781 g.ActiveIdPreviousFrameIsAlive = false; 3782 g.ActiveIdIsJustActivated = false; 3783 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) 3784 g.TempInputId = 0; 3785 if (g.ActiveId == 0) 3786 { 3787 g.ActiveIdUsingNavDirMask = 0x00; 3788 g.ActiveIdUsingNavInputMask = 0x00; 3789 g.ActiveIdUsingKeyInputMask = 0x00; 3790 } 3791 3792 // Drag and drop 3793 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; 3794 g.DragDropAcceptIdCurr = 0; 3795 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 3796 g.DragDropWithinSource = false; 3797 g.DragDropWithinTarget = false; 3798 3799 // Update keyboard input state 3800 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools 3801 g.IO.KeyMods = GetMergedKeyModFlags(); 3802 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); 3803 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) 3804 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; 3805 3806 // Update gamepad/keyboard navigation 3807 NavUpdate(); 3808 3809 // Update mouse input state 3810 UpdateMouseInputs(); 3811 3812 // Find hovered window 3813 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) 3814 UpdateHoveredWindowAndCaptureFlags(); 3815 3816 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) 3817 UpdateMouseMovingWindowNewFrame(); 3818 3819 // Background darkening/whitening 3820 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) 3821 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); 3822 else 3823 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); 3824 3825 g.MouseCursor = ImGuiMouseCursor_Arrow; 3826 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; 3827 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default 3828 3829 // Mouse wheel scrolling, scale 3830 UpdateMouseWheel(); 3831 3832 // Update legacy TAB focus 3833 UpdateTabFocus(); 3834 3835 // Mark all windows as not visible and compact unused memory. 3836 IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); 3837 const float memory_compact_start_time = (g.IO.ConfigWindowsMemoryCompactTimer >= 0.0f) ? (float)g.Time - g.IO.ConfigWindowsMemoryCompactTimer : FLT_MAX; 3838 for (int i = 0; i != g.Windows.Size; i++) 3839 { 3840 ImGuiWindow* window = g.Windows[i]; 3841 window->WasActive = window->Active; 3842 window->BeginCount = 0; 3843 window->Active = false; 3844 window->WriteAccessed = false; 3845 3846 // Garbage collect transient buffers of recently unused windows 3847 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) 3848 GcCompactTransientWindowBuffers(window); 3849 } 3850 3851 // Closing the focused window restore focus to the first active root window in descending z-order 3852 if (g.NavWindow && !g.NavWindow->WasActive) 3853 FocusTopMostWindowUnderOne(NULL, NULL); 3854 3855 // No window should be open at the beginning of the frame. 3856 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. 3857 g.CurrentWindowStack.resize(0); 3858 g.BeginPopupStack.resize(0); 3859 ClosePopupsOverWindow(g.NavWindow, false); 3860 3861 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. 3862 UpdateDebugToolItemPicker(); 3863 3864 // Create implicit/fallback window - which we will only render it if the user has added something to it. 3865 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. 3866 // This fallback is particularly important as it avoid ImGui:: calls from crashing. 3867 g.WithinFrameScopeWithImplicitWindow = true; 3868 SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); 3869 Begin("Debug##Default"); 3870 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); 3871 3872#ifdef IMGUI_ENABLE_TEST_ENGINE 3873 ImGuiTestEngineHook_PostNewFrame(&g); 3874#endif 3875} 3876 3877// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. 3878void ImGui::UpdateDebugToolItemPicker() 3879{ 3880 ImGuiContext& g = *GImGui; 3881 g.DebugItemPickerBreakId = 0; 3882 if (g.DebugItemPickerActive) 3883 { 3884 const ImGuiID hovered_id = g.HoveredIdPreviousFrame; 3885 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); 3886 if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) 3887 g.DebugItemPickerActive = false; 3888 if (ImGui::IsMouseClicked(0) && hovered_id) 3889 { 3890 g.DebugItemPickerBreakId = hovered_id; 3891 g.DebugItemPickerActive = false; 3892 } 3893 ImGui::SetNextWindowBgAlpha(0.60f); 3894 ImGui::BeginTooltip(); 3895 ImGui::Text("HoveredId: 0x%08X", hovered_id); 3896 ImGui::Text("Press ESC to abort picking."); 3897 ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); 3898 ImGui::EndTooltip(); 3899 } 3900} 3901 3902void ImGui::Initialize(ImGuiContext* context) 3903{ 3904 ImGuiContext& g = *context; 3905 IM_ASSERT(!g.Initialized && !g.SettingsLoaded); 3906 3907 // Add .ini handle for ImGuiWindow type 3908 { 3909 ImGuiSettingsHandler ini_handler; 3910 ini_handler.TypeName = "Window"; 3911 ini_handler.TypeHash = ImHashStr("Window"); 3912 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; 3913 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; 3914 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; 3915 g.SettingsHandlers.push_back(ini_handler); 3916 } 3917 3918#ifdef IMGUI_HAS_TABLE 3919 // Add .ini handle for ImGuiTable type 3920 { 3921 ImGuiSettingsHandler ini_handler; 3922 ini_handler.TypeName = "Table"; 3923 ini_handler.TypeHash = ImHashStr("Table"); 3924 ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; 3925 ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; 3926 ini_handler.WriteAllFn = TableSettingsHandler_WriteAll; 3927 g.SettingsHandlers.push_back(ini_handler); 3928 } 3929#endif // #ifdef IMGUI_HAS_TABLE 3930 3931#ifdef IMGUI_HAS_DOCK 3932#endif // #ifdef IMGUI_HAS_DOCK 3933 3934 g.Initialized = true; 3935} 3936 3937// This function is merely here to free heap allocations. 3938void ImGui::Shutdown(ImGuiContext* context) 3939{ 3940 // 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) 3941 ImGuiContext& g = *context; 3942 if (g.IO.Fonts && g.FontAtlasOwnedByContext) 3943 { 3944 g.IO.Fonts->Locked = false; 3945 IM_DELETE(g.IO.Fonts); 3946 } 3947 g.IO.Fonts = NULL; 3948 3949 // Cleanup of other data are conditional on actually having initialized Dear ImGui. 3950 if (!g.Initialized) 3951 return; 3952 3953 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) 3954 if (g.SettingsLoaded && g.IO.IniFilename != NULL) 3955 { 3956 ImGuiContext* backup_context = GImGui; 3957 SetCurrentContext(context); 3958 SaveIniSettingsToDisk(g.IO.IniFilename); 3959 SetCurrentContext(backup_context); 3960 } 3961 3962 // Clear everything else 3963 for (int i = 0; i < g.Windows.Size; i++) 3964 IM_DELETE(g.Windows[i]); 3965 g.Windows.clear(); 3966 g.WindowsFocusOrder.clear(); 3967 g.WindowsTempSortBuffer.clear(); 3968 g.CurrentWindow = NULL; 3969 g.CurrentWindowStack.clear(); 3970 g.WindowsById.Clear(); 3971 g.NavWindow = NULL; 3972 g.HoveredWindow = g.HoveredRootWindow = NULL; 3973 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; 3974 g.MovingWindow = NULL; 3975 g.ColorModifiers.clear(); 3976 g.StyleModifiers.clear(); 3977 g.FontStack.clear(); 3978 g.OpenPopupStack.clear(); 3979 g.BeginPopupStack.clear(); 3980 g.DrawDataBuilder.ClearFreeMemory(); 3981 g.BackgroundDrawList.ClearFreeMemory(); 3982 g.ForegroundDrawList.ClearFreeMemory(); 3983 3984 g.TabBars.Clear(); 3985 g.CurrentTabBarStack.clear(); 3986 g.ShrinkWidthBuffer.clear(); 3987 3988 g.ClipboardHandlerData.clear(); 3989 g.MenusIdSubmittedThisFrame.clear(); 3990 g.InputTextState.ClearFreeMemory(); 3991 3992 g.SettingsWindows.clear(); 3993 g.SettingsHandlers.clear(); 3994 3995 if (g.LogFile) 3996 { 3997#ifndef IMGUI_DISABLE_TTY_FUNCTIONS 3998 if (g.LogFile != stdout) 3999#endif 4000 ImFileClose(g.LogFile); 4001 g.LogFile = NULL; 4002 } 4003 g.LogBuffer.clear(); 4004 4005 g.Initialized = false; 4006} 4007 4008// FIXME: Add a more explicit sort order in the window structure. 4009static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) 4010{ 4011 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; 4012 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; 4013 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) 4014 return d; 4015 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) 4016 return d; 4017 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); 4018} 4019 4020static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window) 4021{ 4022 out_sorted_windows->push_back(window); 4023 if (window->Active) 4024 { 4025 int count = window->DC.ChildWindows.Size; 4026 if (count > 1) 4027 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); 4028 for (int i = 0; i < count; i++) 4029 { 4030 ImGuiWindow* child = window->DC.ChildWindows[i]; 4031 if (child->Active) 4032 AddWindowToSortBuffer(out_sorted_windows, child); 4033 } 4034 } 4035} 4036 4037static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list) 4038{ 4039 if (draw_list->CmdBuffer.empty()) 4040 return; 4041 4042 // Remove trailing command if unused 4043 ImDrawCmd& last_cmd = draw_list->CmdBuffer.back(); 4044 if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL) 4045 { 4046 draw_list->CmdBuffer.pop_back(); 4047 if (draw_list->CmdBuffer.empty()) 4048 return; 4049 } 4050 4051 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. 4052 // May trigger for you if you are using PrimXXX functions incorrectly. 4053 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); 4054 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); 4055 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) 4056 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); 4057 4058 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) 4059 // If this assert triggers because you are drawing lots of stuff manually: 4060 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. 4061 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics window to inspect draw list contents. 4062 // - If you want large meshes with more than 64K vertices, you can either: 4063 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer back-end, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. 4064 // Most example back-ends already support this from 1.71. Pre-1.71 back-ends won't. 4065 // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. 4066 // (B) Or handle 32-bit indices in your renderer back-end, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. 4067 // Most example back-ends already support this. For example, the OpenGL example code detect index size at compile-time: 4068 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); 4069 // Your own engine or render API may use different parameters or function calls to specify index sizes. 4070 // 2 and 4 bytes indices are generally supported by most graphics API. 4071 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching 4072 // the 64K limit to split your draw commands in multiple draw lists. 4073 if (sizeof(ImDrawIdx) == 2) 4074 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); 4075 4076 out_list->push_back(draw_list); 4077} 4078 4079static void AddWindowToDrawData(ImVector<ImDrawList*>* out_render_list, ImGuiWindow* window) 4080{ 4081 ImGuiContext& g = *GImGui; 4082 g.IO.MetricsRenderWindows++; 4083 AddDrawListToDrawData(out_render_list, window->DrawList); 4084 for (int i = 0; i < window->DC.ChildWindows.Size; i++) 4085 { 4086 ImGuiWindow* child = window->DC.ChildWindows[i]; 4087 if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active 4088 AddWindowToDrawData(out_render_list, child); 4089 } 4090} 4091 4092// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) 4093static void AddRootWindowToDrawData(ImGuiWindow* window) 4094{ 4095 ImGuiContext& g = *GImGui; 4096 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; 4097 AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window); 4098} 4099 4100void ImDrawDataBuilder::FlattenIntoSingleLayer() 4101{ 4102 int n = Layers[0].Size; 4103 int size = n; 4104 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) 4105 size += Layers[i].Size; 4106 Layers[0].resize(size); 4107 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) 4108 { 4109 ImVector<ImDrawList*>& layer = Layers[layer_n]; 4110 if (layer.empty()) 4111 continue; 4112 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); 4113 n += layer.Size; 4114 layer.resize(0); 4115 } 4116} 4117 4118static void SetupDrawData(ImVector<ImDrawList*>* draw_lists, ImDrawData* draw_data) 4119{ 4120 ImGuiIO& io = ImGui::GetIO(); 4121 draw_data->Valid = true; 4122 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; 4123 draw_data->CmdListsCount = draw_lists->Size; 4124 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; 4125 draw_data->DisplayPos = ImVec2(0.0f, 0.0f); 4126 draw_data->DisplaySize = io.DisplaySize; 4127 draw_data->FramebufferScale = io.DisplayFramebufferScale; 4128 for (int n = 0; n < draw_lists->Size; n++) 4129 { 4130 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; 4131 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; 4132 } 4133} 4134 4135// 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. 4136void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) 4137{ 4138 ImGuiWindow* window = GetCurrentWindow(); 4139 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); 4140 window->ClipRect = window->DrawList->_ClipRectStack.back(); 4141} 4142 4143void ImGui::PopClipRect() 4144{ 4145 ImGuiWindow* window = GetCurrentWindow(); 4146 window->DrawList->PopClipRect(); 4147 window->ClipRect = window->DrawList->_ClipRectStack.back(); 4148} 4149 4150// 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. 4151void ImGui::EndFrame() 4152{ 4153 ImGuiContext& g = *GImGui; 4154 IM_ASSERT(g.Initialized); 4155 4156 // Don't process EndFrame() multiple times. 4157 if (g.FrameCountEnded == g.FrameCount) 4158 return; 4159 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?"); 4160 4161 ErrorCheckEndFrameSanityChecks(); 4162 4163 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) 4164 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) 4165 { 4166 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); 4167 g.PlatformImeLastPos = g.PlatformImePos; 4168 } 4169 4170 // Hide implicit/fallback "Debug" window if it hasn't been used 4171 g.WithinFrameScopeWithImplicitWindow = false; 4172 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) 4173 g.CurrentWindow->Active = false; 4174 End(); 4175 4176 // Show CTRL+TAB list window 4177 if (g.NavWindowingTarget != NULL) 4178 NavUpdateWindowingOverlay(); 4179 4180 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) 4181 if (g.DragDropActive) 4182 { 4183 bool is_delivered = g.DragDropPayload.Delivery; 4184 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); 4185 if (is_delivered || is_elapsed) 4186 ClearDragDrop(); 4187 } 4188 4189 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. 4190 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount) 4191 { 4192 g.DragDropWithinSource = true; 4193 SetTooltip("..."); 4194 g.DragDropWithinSource = false; 4195 } 4196 4197 // End frame 4198 g.WithinFrameScope = false; 4199 g.FrameCountEnded = g.FrameCount; 4200 4201 // Initiate moving window + handle left-click and right-click focus 4202 UpdateMouseMovingWindowEndFrame(); 4203 4204 // Sort the window list so that all child windows are after their parent 4205 // We cannot do that on FocusWindow() because childs may not exist yet 4206 g.WindowsTempSortBuffer.resize(0); 4207 g.WindowsTempSortBuffer.reserve(g.Windows.Size); 4208 for (int i = 0; i != g.Windows.Size; i++) 4209 { 4210 ImGuiWindow* window = g.Windows[i]; 4211 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it 4212 continue; 4213 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window); 4214 } 4215 4216 // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong. 4217 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size); 4218 g.Windows.swap(g.WindowsTempSortBuffer); 4219 g.IO.MetricsActiveWindows = g.WindowsActiveCount; 4220 4221 // Unlock font atlas 4222 g.IO.Fonts->Locked = false; 4223 4224 // Clear Input data for next frame 4225 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; 4226 g.IO.InputQueueCharacters.resize(0); 4227 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); 4228} 4229 4230void ImGui::Render() 4231{ 4232 ImGuiContext& g = *GImGui; 4233 IM_ASSERT(g.Initialized); 4234 4235 if (g.FrameCountEnded != g.FrameCount) 4236 EndFrame(); 4237 g.FrameCountRendered = g.FrameCount; 4238 g.IO.MetricsRenderWindows = 0; 4239 g.DrawDataBuilder.Clear(); 4240 4241 // Add background ImDrawList 4242 if (!g.BackgroundDrawList.VtxBuffer.empty()) 4243 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); 4244 4245 // Add ImDrawList to render 4246 ImGuiWindow* windows_to_render_top_most[2]; 4247 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; 4248 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingList : NULL); 4249 for (int n = 0; n != g.Windows.Size; n++) 4250 { 4251 ImGuiWindow* window = g.Windows[n]; 4252 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) 4253 AddRootWindowToDrawData(window); 4254 } 4255 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) 4256 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window 4257 AddRootWindowToDrawData(windows_to_render_top_most[n]); 4258 g.DrawDataBuilder.FlattenIntoSingleLayer(); 4259 4260 // Draw software mouse cursor if requested 4261 if (g.IO.MouseDrawCursor) 4262 RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); 4263 4264 // Add foreground ImDrawList 4265 if (!g.ForegroundDrawList.VtxBuffer.empty()) 4266 AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); 4267 4268 // Setup ImDrawData structure for end-user 4269 SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); 4270 g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; 4271 g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; 4272 4273 // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves. 4274#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS 4275 if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) 4276 g.IO.RenderDrawListsFn(&g.DrawData); 4277#endif 4278} 4279 4280// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. 4281// CalcTextSize("") should return ImVec2(0.0f, g.FontSize) 4282ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) 4283{ 4284 ImGuiContext& g = *GImGui; 4285 4286 const char* text_display_end; 4287 if (hide_text_after_double_hash) 4288 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string 4289 else 4290 text_display_end = text_end; 4291 4292 ImFont* font = g.Font; 4293 const float font_size = g.FontSize; 4294 if (text == text_display_end) 4295 return ImVec2(0.0f, font_size); 4296 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); 4297 4298 // Round 4299 text_size.x = IM_FLOOR(text_size.x + 0.95f); 4300 4301 return text_size; 4302} 4303 4304// Find window given position, search front-to-back 4305// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically 4306// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is 4307// called, aka before the next Begin(). Moving window isn't affected. 4308static void FindHoveredWindow() 4309{ 4310 ImGuiContext& g = *GImGui; 4311 4312 ImGuiWindow* hovered_window = NULL; 4313 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) 4314 hovered_window = g.MovingWindow; 4315 4316 ImVec2 padding_regular = g.Style.TouchExtraPadding; 4317 ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular; 4318 for (int i = g.Windows.Size - 1; i >= 0; i--) 4319 { 4320 ImGuiWindow* window = g.Windows[i]; 4321 if (!window->Active || window->Hidden) 4322 continue; 4323 if (window->Flags & ImGuiWindowFlags_NoMouseInputs) 4324 continue; 4325 4326 // Using the clipped AABB, a child window will typically be clipped by its parent (not always) 4327 ImRect bb(window->OuterRectClipped); 4328 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) 4329 bb.Expand(padding_regular); 4330 else 4331 bb.Expand(padding_for_resize_from_edges); 4332 if (!bb.Contains(g.IO.MousePos)) 4333 continue; 4334 4335 // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches. 4336 if (hovered_window == NULL) 4337 hovered_window = window; 4338 if (hovered_window) 4339 break; 4340 } 4341 4342 g.HoveredWindow = hovered_window; 4343 g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; 4344 4345} 4346 4347// Test if mouse cursor is hovering given rectangle 4348// NB- Rectangle is clipped by our current clip setting 4349// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) 4350bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) 4351{ 4352 ImGuiContext& g = *GImGui; 4353 4354 // Clip 4355 ImRect rect_clipped(r_min, r_max); 4356 if (clip) 4357 rect_clipped.ClipWith(g.CurrentWindow->ClipRect); 4358 4359 // Expand for touch input 4360 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); 4361 if (!rect_for_touch.Contains(g.IO.MousePos)) 4362 return false; 4363 return true; 4364} 4365 4366int ImGui::GetKeyIndex(ImGuiKey imgui_key) 4367{ 4368 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); 4369 ImGuiContext& g = *GImGui; 4370 return g.IO.KeyMap[imgui_key]; 4371} 4372 4373// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! 4374// Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]! 4375bool ImGui::IsKeyDown(int user_key_index) 4376{ 4377 if (user_key_index < 0) 4378 return false; 4379 ImGuiContext& g = *GImGui; 4380 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4381 return g.IO.KeysDown[user_key_index]; 4382} 4383 4384// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) 4385// t1 = current time (e.g.: g.Time) 4386// An event is triggered at: 4387// t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N 4388int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) 4389{ 4390 if (t1 == 0.0f) 4391 return 1; 4392 if (t0 >= t1) 4393 return 0; 4394 if (repeat_rate <= 0.0f) 4395 return (t0 < repeat_delay) && (t1 >= repeat_delay); 4396 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); 4397 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); 4398 const int count = count_t1 - count_t0; 4399 return count; 4400} 4401 4402int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) 4403{ 4404 ImGuiContext& g = *GImGui; 4405 if (key_index < 0) 4406 return 0; 4407 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4408 const float t = g.IO.KeysDownDuration[key_index]; 4409 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); 4410} 4411 4412bool ImGui::IsKeyPressed(int user_key_index, bool repeat) 4413{ 4414 ImGuiContext& g = *GImGui; 4415 if (user_key_index < 0) 4416 return false; 4417 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4418 const float t = g.IO.KeysDownDuration[user_key_index]; 4419 if (t == 0.0f) 4420 return true; 4421 if (repeat && t > g.IO.KeyRepeatDelay) 4422 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; 4423 return false; 4424} 4425 4426bool ImGui::IsKeyReleased(int user_key_index) 4427{ 4428 ImGuiContext& g = *GImGui; 4429 if (user_key_index < 0) return false; 4430 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4431 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; 4432} 4433 4434bool ImGui::IsMouseDown(ImGuiMouseButton button) 4435{ 4436 ImGuiContext& g = *GImGui; 4437 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4438 return g.IO.MouseDown[button]; 4439} 4440 4441bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) 4442{ 4443 ImGuiContext& g = *GImGui; 4444 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4445 const float t = g.IO.MouseDownDuration[button]; 4446 if (t == 0.0f) 4447 return true; 4448 4449 if (repeat && t > g.IO.KeyRepeatDelay) 4450 { 4451 // FIXME: 2019/05/03: Our old repeat code was wrong here and led to doubling the repeat rate, which made it an ok rate for repeat on mouse hold. 4452 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); 4453 if (amount > 0) 4454 return true; 4455 } 4456 return false; 4457} 4458 4459bool ImGui::IsMouseReleased(ImGuiMouseButton button) 4460{ 4461 ImGuiContext& g = *GImGui; 4462 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4463 return g.IO.MouseReleased[button]; 4464} 4465 4466bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) 4467{ 4468 ImGuiContext& g = *GImGui; 4469 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4470 return g.IO.MouseDoubleClicked[button]; 4471} 4472 4473// [Internal] This doesn't test if the button is pressed 4474bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) 4475{ 4476 ImGuiContext& g = *GImGui; 4477 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4478 if (lock_threshold < 0.0f) 4479 lock_threshold = g.IO.MouseDragThreshold; 4480 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; 4481} 4482 4483bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) 4484{ 4485 ImGuiContext& g = *GImGui; 4486 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4487 if (!g.IO.MouseDown[button]) 4488 return false; 4489 return IsMouseDragPastThreshold(button, lock_threshold); 4490} 4491 4492ImVec2 ImGui::GetMousePos() 4493{ 4494 ImGuiContext& g = *GImGui; 4495 return g.IO.MousePos; 4496} 4497 4498// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! 4499ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() 4500{ 4501 ImGuiContext& g = *GImGui; 4502 if (g.BeginPopupStack.Size > 0) 4503 return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos; 4504 return g.IO.MousePos; 4505} 4506 4507// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. 4508bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) 4509{ 4510 // The assert is only to silence a false-positive in XCode Static Analysis. 4511 // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). 4512 IM_ASSERT(GImGui != NULL); 4513 const float MOUSE_INVALID = -256000.0f; 4514 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; 4515 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; 4516} 4517 4518bool ImGui::IsAnyMouseDown() 4519{ 4520 ImGuiContext& g = *GImGui; 4521 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) 4522 if (g.IO.MouseDown[n]) 4523 return true; 4524 return false; 4525} 4526 4527// Return the delta from the initial clicking position while the mouse button is clicked or was just released. 4528// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. 4529// NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window. 4530ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) 4531{ 4532 ImGuiContext& g = *GImGui; 4533 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4534 if (lock_threshold < 0.0f) 4535 lock_threshold = g.IO.MouseDragThreshold; 4536 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) 4537 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) 4538 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) 4539 return g.IO.MousePos - g.IO.MouseClickedPos[button]; 4540 return ImVec2(0.0f, 0.0f); 4541} 4542 4543void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) 4544{ 4545 ImGuiContext& g = *GImGui; 4546 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4547 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr 4548 g.IO.MouseClickedPos[button] = g.IO.MousePos; 4549} 4550 4551ImGuiMouseCursor ImGui::GetMouseCursor() 4552{ 4553 return GImGui->MouseCursor; 4554} 4555 4556void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) 4557{ 4558 GImGui->MouseCursor = cursor_type; 4559} 4560 4561void ImGui::CaptureKeyboardFromApp(bool capture) 4562{ 4563 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0; 4564} 4565 4566void ImGui::CaptureMouseFromApp(bool capture) 4567{ 4568 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0; 4569} 4570 4571bool ImGui::IsItemActive() 4572{ 4573 ImGuiContext& g = *GImGui; 4574 if (g.ActiveId) 4575 { 4576 ImGuiWindow* window = g.CurrentWindow; 4577 return g.ActiveId == window->DC.LastItemId; 4578 } 4579 return false; 4580} 4581 4582bool ImGui::IsItemActivated() 4583{ 4584 ImGuiContext& g = *GImGui; 4585 if (g.ActiveId) 4586 { 4587 ImGuiWindow* window = g.CurrentWindow; 4588 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) 4589 return true; 4590 } 4591 return false; 4592} 4593 4594bool ImGui::IsItemDeactivated() 4595{ 4596 ImGuiContext& g = *GImGui; 4597 ImGuiWindow* window = g.CurrentWindow; 4598 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) 4599 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; 4600 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); 4601} 4602 4603bool ImGui::IsItemDeactivatedAfterEdit() 4604{ 4605 ImGuiContext& g = *GImGui; 4606 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); 4607} 4608 4609bool ImGui::IsItemFocused() 4610{ 4611 ImGuiContext& g = *GImGui; 4612 ImGuiWindow* window = g.CurrentWindow; 4613 4614 if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId) 4615 return false; 4616 return true; 4617} 4618 4619bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) 4620{ 4621 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); 4622} 4623 4624bool ImGui::IsItemToggledOpen() 4625{ 4626 ImGuiContext& g = *GImGui; 4627 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; 4628} 4629 4630bool ImGui::IsItemToggledSelection() 4631{ 4632 ImGuiContext& g = *GImGui; 4633 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; 4634} 4635 4636bool ImGui::IsAnyItemHovered() 4637{ 4638 ImGuiContext& g = *GImGui; 4639 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; 4640} 4641 4642bool ImGui::IsAnyItemActive() 4643{ 4644 ImGuiContext& g = *GImGui; 4645 return g.ActiveId != 0; 4646} 4647 4648bool ImGui::IsAnyItemFocused() 4649{ 4650 ImGuiContext& g = *GImGui; 4651 return g.NavId != 0 && !g.NavDisableHighlight; 4652} 4653 4654bool ImGui::IsItemVisible() 4655{ 4656 ImGuiWindow* window = GetCurrentWindowRead(); 4657 return window->ClipRect.Overlaps(window->DC.LastItemRect); 4658} 4659 4660bool ImGui::IsItemEdited() 4661{ 4662 ImGuiWindow* window = GetCurrentWindowRead(); 4663 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; 4664} 4665 4666// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. 4667void ImGui::SetItemAllowOverlap() 4668{ 4669 ImGuiContext& g = *GImGui; 4670 if (g.HoveredId == g.CurrentWindow->DC.LastItemId) 4671 g.HoveredIdAllowOverlap = true; 4672 if (g.ActiveId == g.CurrentWindow->DC.LastItemId) 4673 g.ActiveIdAllowOverlap = true; 4674} 4675 4676ImVec2 ImGui::GetItemRectMin() 4677{ 4678 ImGuiWindow* window = GetCurrentWindowRead(); 4679 return window->DC.LastItemRect.Min; 4680} 4681 4682ImVec2 ImGui::GetItemRectMax() 4683{ 4684 ImGuiWindow* window = GetCurrentWindowRead(); 4685 return window->DC.LastItemRect.Max; 4686} 4687 4688ImVec2 ImGui::GetItemRectSize() 4689{ 4690 ImGuiWindow* window = GetCurrentWindowRead(); 4691 return window->DC.LastItemRect.GetSize(); 4692} 4693 4694static ImRect GetViewportRect() 4695{ 4696 ImGuiContext& g = *GImGui; 4697 return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); 4698} 4699 4700bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) 4701{ 4702 ImGuiContext& g = *GImGui; 4703 ImGuiWindow* parent_window = g.CurrentWindow; 4704 4705 flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; 4706 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag 4707 4708 // Size 4709 const ImVec2 content_avail = GetContentRegionAvail(); 4710 ImVec2 size = ImFloor(size_arg); 4711 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); 4712 if (size.x <= 0.0f) 4713 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) 4714 if (size.y <= 0.0f) 4715 size.y = ImMax(content_avail.y + size.y, 4.0f); 4716 SetNextWindowSize(size); 4717 4718 // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. 4719 char title[256]; 4720 if (name) 4721 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id); 4722 else 4723 ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id); 4724 4725 const float backup_border_size = g.Style.ChildBorderSize; 4726 if (!border) 4727 g.Style.ChildBorderSize = 0.0f; 4728 bool ret = Begin(title, NULL, flags); 4729 g.Style.ChildBorderSize = backup_border_size; 4730 4731 ImGuiWindow* child_window = g.CurrentWindow; 4732 child_window->ChildId = id; 4733 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises; 4734 4735 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. 4736 // While this is not really documented/defined, it seems that the expected thing to do. 4737 if (child_window->BeginCount == 1) 4738 parent_window->DC.CursorPos = child_window->Pos; 4739 4740 // Process navigation-in immediately so NavInit can run on first frame 4741 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) 4742 { 4743 FocusWindow(child_window); 4744 NavInitWindow(child_window, false); 4745 SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item 4746 g.ActiveIdSource = ImGuiInputSource_Nav; 4747 } 4748 return ret; 4749} 4750 4751bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 4752{ 4753 ImGuiWindow* window = GetCurrentWindow(); 4754 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); 4755} 4756 4757bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 4758{ 4759 IM_ASSERT(id != 0); 4760 return BeginChildEx(NULL, id, size_arg, border, extra_flags); 4761} 4762 4763void ImGui::EndChild() 4764{ 4765 ImGuiContext& g = *GImGui; 4766 ImGuiWindow* window = g.CurrentWindow; 4767 4768 IM_ASSERT(g.WithinEndChild == false); 4769 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls 4770 4771 g.WithinEndChild = true; 4772 if (window->BeginCount > 1) 4773 { 4774 End(); 4775 } 4776 else 4777 { 4778 ImVec2 sz = window->Size; 4779 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f 4780 sz.x = ImMax(4.0f, sz.x); 4781 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) 4782 sz.y = ImMax(4.0f, sz.y); 4783 End(); 4784 4785 ImGuiWindow* parent_window = g.CurrentWindow; 4786 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); 4787 ItemSize(sz); 4788 if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) 4789 { 4790 ItemAdd(bb, window->ChildId); 4791 RenderNavHighlight(bb, window->ChildId); 4792 4793 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child 4794 if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) 4795 RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); 4796 } 4797 else 4798 { 4799 // Not navigable into 4800 ItemAdd(bb, 0); 4801 } 4802 } 4803 g.WithinEndChild = false; 4804} 4805 4806// Helper to create a child window / scrolling region that looks like a normal widget frame. 4807bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) 4808{ 4809 ImGuiContext& g = *GImGui; 4810 const ImGuiStyle& style = g.Style; 4811 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); 4812 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); 4813 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); 4814 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); 4815 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); 4816 PopStyleVar(3); 4817 PopStyleColor(); 4818 return ret; 4819} 4820 4821void ImGui::EndChildFrame() 4822{ 4823 EndChild(); 4824} 4825 4826static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) 4827{ 4828 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); 4829 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); 4830 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); 4831} 4832 4833ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) 4834{ 4835 ImGuiContext& g = *GImGui; 4836 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); 4837} 4838 4839ImGuiWindow* ImGui::FindWindowByName(const char* name) 4840{ 4841 ImGuiID id = ImHashStr(name); 4842 return FindWindowByID(id); 4843} 4844 4845static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) 4846{ 4847 ImGuiContext& g = *GImGui; 4848 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); 4849 4850 // Create window the first time 4851 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); 4852 window->Flags = flags; 4853 g.WindowsById.SetVoidPtr(window->ID, window); 4854 4855 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. 4856 window->Pos = ImVec2(60, 60); 4857 4858 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. 4859 if (!(flags & ImGuiWindowFlags_NoSavedSettings)) 4860 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) 4861 { 4862 // Retrieve settings from .ini file 4863 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); 4864 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); 4865 window->Pos = ImVec2(settings->Pos.x, settings->Pos.y); 4866 window->Collapsed = settings->Collapsed; 4867 if (settings->Size.x > 0 && settings->Size.y > 0) 4868 size = ImVec2(settings->Size.x, settings->Size.y); 4869 } 4870 window->Size = window->SizeFull = ImFloor(size); 4871 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values 4872 4873 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) 4874 { 4875 window->AutoFitFramesX = window->AutoFitFramesY = 2; 4876 window->AutoFitOnlyGrows = false; 4877 } 4878 else 4879 { 4880 if (window->Size.x <= 0.0f) 4881 window->AutoFitFramesX = 2; 4882 if (window->Size.y <= 0.0f) 4883 window->AutoFitFramesY = 2; 4884 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); 4885 } 4886 4887 g.WindowsFocusOrder.push_back(window); 4888 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) 4889 g.Windows.push_front(window); // Quite slow but rare and only once 4890 else 4891 g.Windows.push_back(window); 4892 return window; 4893} 4894 4895static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) 4896{ 4897 ImGuiContext& g = *GImGui; 4898 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) 4899 { 4900 // Using -1,-1 on either X/Y axis to preserve the current size. 4901 ImRect cr = g.NextWindowData.SizeConstraintRect; 4902 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; 4903 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; 4904 if (g.NextWindowData.SizeCallback) 4905 { 4906 ImGuiSizeCallbackData data; 4907 data.UserData = g.NextWindowData.SizeCallbackUserData; 4908 data.Pos = window->Pos; 4909 data.CurrentSize = window->SizeFull; 4910 data.DesiredSize = new_size; 4911 g.NextWindowData.SizeCallback(&data); 4912 new_size = data.DesiredSize; 4913 } 4914 new_size.x = IM_FLOOR(new_size.x); 4915 new_size.y = IM_FLOOR(new_size.y); 4916 } 4917 4918 // Minimum size 4919 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) 4920 { 4921 ImGuiWindow* window_for_height = window; 4922 new_size = ImMax(new_size, g.Style.WindowMinSize); 4923 new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows 4924 } 4925 return new_size; 4926} 4927 4928static ImVec2 CalcWindowContentSize(ImGuiWindow* window) 4929{ 4930 if (window->Collapsed) 4931 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 4932 return window->ContentSize; 4933 if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0) 4934 return window->ContentSize; 4935 4936 ImVec2 sz; 4937 sz.x = IM_FLOOR((window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); 4938 sz.y = IM_FLOOR((window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); 4939 return sz; 4940} 4941 4942static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) 4943{ 4944 ImGuiContext& g = *GImGui; 4945 ImGuiStyle& style = g.Style; 4946 ImVec2 size_decorations = ImVec2(0.0f, window->TitleBarHeight() + window->MenuBarHeight()); 4947 ImVec2 size_pad = window->WindowPadding * 2.0f; 4948 ImVec2 size_desired = size_contents + size_pad + size_decorations; 4949 if (window->Flags & ImGuiWindowFlags_Tooltip) 4950 { 4951 // Tooltip always resize 4952 return size_desired; 4953 } 4954 else 4955 { 4956 // Maximum window size is determined by the viewport size or monitor size 4957 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; 4958 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; 4959 ImVec2 size_min = style.WindowMinSize; 4960 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) 4961 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); 4962 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f)); 4963 4964 // When the window cannot fit all contents (either because of constraints, either because screen is too small), 4965 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. 4966 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); 4967 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - size_decorations.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); 4968 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - size_decorations.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); 4969 if (will_have_scrollbar_x) 4970 size_auto_fit.y += style.ScrollbarSize; 4971 if (will_have_scrollbar_y) 4972 size_auto_fit.x += style.ScrollbarSize; 4973 return size_auto_fit; 4974 } 4975} 4976 4977ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window) 4978{ 4979 ImVec2 size_contents = CalcWindowContentSize(window); 4980 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents); 4981 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit); 4982 return size_final; 4983} 4984 4985static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) 4986{ 4987 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) 4988 return ImGuiCol_PopupBg; 4989 if (flags & ImGuiWindowFlags_ChildWindow) 4990 return ImGuiCol_ChildBg; 4991 return ImGuiCol_WindowBg; 4992} 4993 4994static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) 4995{ 4996 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left 4997 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right 4998 ImVec2 size_expected = pos_max - pos_min; 4999 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected); 5000 *out_pos = pos_min; 5001 if (corner_norm.x == 0.0f) 5002 out_pos->x -= (size_constrained.x - size_expected.x); 5003 if (corner_norm.y == 0.0f) 5004 out_pos->y -= (size_constrained.y - size_expected.y); 5005 *out_size = size_constrained; 5006} 5007 5008struct ImGuiResizeGripDef 5009{ 5010 ImVec2 CornerPosN; 5011 ImVec2 InnerDir; 5012 int AngleMin12, AngleMax12; 5013}; 5014 5015static const ImGuiResizeGripDef resize_grip_def[4] = 5016{ 5017 { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower-right 5018 { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower-left 5019 { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper-left (Unused) 5020 { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper-right (Unused) 5021}; 5022 5023static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) 5024{ 5025 ImRect rect = window->Rect(); 5026 if (thickness == 0.0f) rect.Max -= ImVec2(1,1); 5027 if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top 5028 if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right 5029 if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom 5030 if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left 5031 IM_ASSERT(0); 5032 return ImRect(); 5033} 5034 5035// 0..3: corners (Lower-right, Lower-left, Unused, Unused) 5036// 4..7: borders (Top, Right, Bottom, Left) 5037ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n) 5038{ 5039 IM_ASSERT(n >= 0 && n <= 7); 5040 ImGuiID id = window->ID; 5041 id = ImHashStr("#RESIZE", 0, id); 5042 id = ImHashData(&n, sizeof(int), id); 5043 return id; 5044} 5045 5046// Handle resize for: Resize Grips, Borders, Gamepad 5047// Return true when using auto-fit (double click on resize grip) 5048static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]) 5049{ 5050 ImGuiContext& g = *GImGui; 5051 ImGuiWindowFlags flags = window->Flags; 5052 5053 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5054 return false; 5055 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. 5056 return false; 5057 5058 bool ret_auto_fit = false; 5059 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; 5060 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); 5061 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); 5062 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f; 5063 5064 ImVec2 pos_target(FLT_MAX, FLT_MAX); 5065 ImVec2 size_target(FLT_MAX, FLT_MAX); 5066 5067 // Resize grips and borders are on layer 1 5068 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; 5069 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); 5070 5071 // Manual resize grips 5072 PushID("#RESIZE"); 5073 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5074 { 5075 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 5076 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); 5077 5078 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window 5079 ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); 5080 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); 5081 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); 5082 bool hovered, held; 5083 ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); 5084 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); 5085 if (hovered || held) 5086 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 5087 5088 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) 5089 { 5090 // Manual auto-fit when double-clicking 5091 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5092 ret_auto_fit = true; 5093 ClearActiveID(); 5094 } 5095 else if (held) 5096 { 5097 // Resize from any of the four corners 5098 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position 5099 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip 5100 CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); 5101 } 5102 if (resize_grip_n == 0 || held || hovered) 5103 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); 5104 } 5105 for (int border_n = 0; border_n < resize_border_count; border_n++) 5106 { 5107 bool hovered, held; 5108 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); 5109 ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren); 5110 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); 5111 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) 5112 { 5113 g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; 5114 if (held) 5115 *border_held = border_n; 5116 } 5117 if (held) 5118 { 5119 ImVec2 border_target = window->Pos; 5120 ImVec2 border_posn; 5121 if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top 5122 if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right 5123 if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom 5124 if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left 5125 CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); 5126 } 5127 } 5128 PopID(); 5129 5130 // Restore nav layer 5131 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5132 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); 5133 5134 // Navigation resize (keyboard/gamepad) 5135 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) 5136 { 5137 ImVec2 nav_resize_delta; 5138 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) 5139 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 5140 if (g.NavInputSource == ImGuiInputSource_NavGamepad) 5141 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); 5142 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) 5143 { 5144 const float NAV_RESIZE_SPEED = 600.0f; 5145 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); 5146 g.NavWindowingToggleLayer = false; 5147 g.NavDisableMouseHover = true; 5148 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); 5149 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. 5150 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); 5151 } 5152 } 5153 5154 // Apply back modified position/size to window 5155 if (size_target.x != FLT_MAX) 5156 { 5157 window->SizeFull = size_target; 5158 MarkIniSettingsDirty(window); 5159 } 5160 if (pos_target.x != FLT_MAX) 5161 { 5162 window->Pos = ImFloor(pos_target); 5163 MarkIniSettingsDirty(window); 5164 } 5165 5166 window->Size = window->SizeFull; 5167 return ret_auto_fit; 5168} 5169 5170static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding) 5171{ 5172 ImGuiContext& g = *GImGui; 5173 ImVec2 size_for_clamping = (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size; 5174 window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping); 5175} 5176 5177static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) 5178{ 5179 ImGuiContext& g = *GImGui; 5180 float rounding = window->WindowRounding; 5181 float border_size = window->WindowBorderSize; 5182 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) 5183 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); 5184 5185 int border_held = window->ResizeBorderHeld; 5186 if (border_held != -1) 5187 { 5188 struct ImGuiResizeBorderDef 5189 { 5190 ImVec2 InnerDir; 5191 ImVec2 CornerPosN1, CornerPosN2; 5192 float OuterAngle; 5193 }; 5194 static const ImGuiResizeBorderDef resize_border_def[4] = 5195 { 5196 { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top 5197 { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right 5198 { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom 5199 { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left 5200 }; 5201 const ImGuiResizeBorderDef& def = resize_border_def[border_held]; 5202 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); 5203 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle); 5204 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f); 5205 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual 5206 } 5207 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5208 { 5209 float y = window->Pos.y + window->TitleBarHeight() - 1; 5210 window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); 5211 } 5212} 5213 5214// Draw background and borders 5215// Draw and handle scrollbars 5216void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) 5217{ 5218 ImGuiContext& g = *GImGui; 5219 ImGuiStyle& style = g.Style; 5220 ImGuiWindowFlags flags = window->Flags; 5221 5222 // Ensure that ScrollBar doesn't read last frame's SkipItems 5223 window->SkipItems = false; 5224 5225 // Draw window + handle manual resize 5226 // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. 5227 const float window_rounding = window->WindowRounding; 5228 const float window_border_size = window->WindowBorderSize; 5229 if (window->Collapsed) 5230 { 5231 // Title bar only 5232 float backup_border_size = style.FrameBorderSize; 5233 g.Style.FrameBorderSize = window->WindowBorderSize; 5234 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); 5235 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); 5236 g.Style.FrameBorderSize = backup_border_size; 5237 } 5238 else 5239 { 5240 // Window background 5241 if (!(flags & ImGuiWindowFlags_NoBackground)) 5242 { 5243 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); 5244 bool override_alpha = false; 5245 float alpha = 1.0f; 5246 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) 5247 { 5248 alpha = g.NextWindowData.BgAlphaVal; 5249 override_alpha = true; 5250 } 5251 if (override_alpha) 5252 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); 5253 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); 5254 } 5255 5256 // Title bar 5257 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 5258 { 5259 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); 5260 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); 5261 } 5262 5263 // Menu bar 5264 if (flags & ImGuiWindowFlags_MenuBar) 5265 { 5266 ImRect menu_bar_rect = window->MenuBarRect(); 5267 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. 5268 window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); 5269 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) 5270 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); 5271 } 5272 5273 // Scrollbars 5274 if (window->ScrollbarX) 5275 Scrollbar(ImGuiAxis_X); 5276 if (window->ScrollbarY) 5277 Scrollbar(ImGuiAxis_Y); 5278 5279 // Render resize grips (after their input handling so we don't have a frame of latency) 5280 if (!(flags & ImGuiWindowFlags_NoResize)) 5281 { 5282 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5283 { 5284 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 5285 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); 5286 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); 5287 window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); 5288 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); 5289 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); 5290 } 5291 } 5292 5293 // Borders 5294 RenderWindowOuterBorders(window); 5295 } 5296} 5297 5298// Render title text, collapse button, close button 5299void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) 5300{ 5301 ImGuiContext& g = *GImGui; 5302 ImGuiStyle& style = g.Style; 5303 ImGuiWindowFlags flags = window->Flags; 5304 5305 const bool has_close_button = (p_open != NULL); 5306 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); 5307 5308 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) 5309 const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; 5310 window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; 5311 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; 5312 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); 5313 5314 // Layout buttons 5315 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. 5316 float pad_l = style.FramePadding.x; 5317 float pad_r = style.FramePadding.x; 5318 float button_sz = g.FontSize; 5319 ImVec2 close_button_pos; 5320 ImVec2 collapse_button_pos; 5321 if (has_close_button) 5322 { 5323 pad_r += button_sz; 5324 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); 5325 } 5326 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) 5327 { 5328 pad_r += button_sz; 5329 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); 5330 } 5331 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) 5332 { 5333 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); 5334 pad_l += button_sz; 5335 } 5336 5337 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) 5338 if (has_collapse_button) 5339 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) 5340 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function 5341 5342 // Close button 5343 if (has_close_button) 5344 if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) 5345 *p_open = false; 5346 5347 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5348 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); 5349 window->DC.ItemFlags = item_flags_backup; 5350 5351 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) 5352 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. 5353 const char* UNSAVED_DOCUMENT_MARKER = "*"; 5354 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; 5355 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); 5356 5357 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, 5358 // while uncentered title text will still reach edges correct. 5359 if (pad_l > style.FramePadding.x) 5360 pad_l += g.Style.ItemInnerSpacing.x; 5361 if (pad_r > style.FramePadding.x) 5362 pad_r += g.Style.ItemInnerSpacing.x; 5363 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) 5364 { 5365 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center 5366 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); 5367 pad_l = ImMax(pad_l, pad_extend * centerness); 5368 pad_r = ImMax(pad_r, pad_extend * centerness); 5369 } 5370 5371 ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); 5372 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, layout_r.Max.x + g.Style.ItemInnerSpacing.x, layout_r.Max.y); 5373 //if (g.IO.KeyCtrl) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] 5374 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); 5375 if (flags & ImGuiWindowFlags_UnsavedDocument) 5376 { 5377 ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); 5378 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); 5379 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); 5380 } 5381} 5382 5383void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) 5384{ 5385 window->ParentWindow = parent_window; 5386 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; 5387 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) 5388 window->RootWindow = parent_window->RootWindow; 5389 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) 5390 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; 5391 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) 5392 { 5393 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); 5394 window->RootWindowForNav = window->RootWindowForNav->ParentWindow; 5395 } 5396} 5397 5398// Push a new Dear ImGui window to add widgets to. 5399// - 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. 5400// - Begin/End can be called multiple times during the frame with the same window name to append content. 5401// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). 5402// 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. 5403// - 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. 5404// - 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. 5405bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) 5406{ 5407 ImGuiContext& g = *GImGui; 5408 const ImGuiStyle& style = g.Style; 5409 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required 5410 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame() 5411 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet 5412 5413 // Find or create 5414 ImGuiWindow* window = FindWindowByName(name); 5415 const bool window_just_created = (window == NULL); 5416 if (window_just_created) 5417 { 5418 ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. 5419 window = CreateNewWindow(name, size_on_first_use, flags); 5420 } 5421 5422 // Automatically disable manual moving/resizing when NoInputs is set 5423 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) 5424 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 5425 5426 if (flags & ImGuiWindowFlags_NavFlattened) 5427 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); 5428 5429 const int current_frame = g.FrameCount; 5430 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); 5431 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); 5432 5433 // Update the Appearing flag 5434 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on 5435 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); 5436 if (flags & ImGuiWindowFlags_Popup) 5437 { 5438 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; 5439 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed 5440 window_just_activated_by_user |= (window != popup_ref.Window); 5441 } 5442 window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); 5443 if (window->Appearing) 5444 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); 5445 5446 // Update Flags, LastFrameActive, BeginOrderXXX fields 5447 if (first_begin_of_the_frame) 5448 { 5449 window->Flags = (ImGuiWindowFlags)flags; 5450 window->LastFrameActive = current_frame; 5451 window->LastTimeActive = (float)g.Time; 5452 window->BeginOrderWithinParent = 0; 5453 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); 5454 } 5455 else 5456 { 5457 flags = window->Flags; 5458 } 5459 5460 // 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 5461 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); 5462 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; 5463 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); 5464 5465 // We allow window memory to be compacted so recreate the base stack when needed. 5466 if (window->IDStack.Size == 0) 5467 window->IDStack.push_back(window->ID); 5468 5469 // Add to stack 5470 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() 5471 g.CurrentWindowStack.push_back(window); 5472 g.CurrentWindow = NULL; 5473 ErrorCheckBeginEndCompareStacksSize(window, true); 5474 if (flags & ImGuiWindowFlags_Popup) 5475 { 5476 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; 5477 popup_ref.Window = window; 5478 g.BeginPopupStack.push_back(popup_ref); 5479 window->PopupId = popup_ref.PopupId; 5480 } 5481 5482 if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow)) 5483 window->NavLastIds[0] = 0; 5484 5485 // Update ->RootWindow and others pointers (before any possible call to FocusWindow) 5486 if (first_begin_of_the_frame) 5487 UpdateWindowParentAndRootLinks(window, flags, parent_window); 5488 5489 // Process SetNextWindow***() calls 5490 bool window_pos_set_by_api = false; 5491 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; 5492 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) 5493 { 5494 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; 5495 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) 5496 { 5497 // May be processed on the next frame if this is our first frame and we are measuring size 5498 // FIXME: Look into removing the branch so everything can go through this same code path for consistency. 5499 window->SetWindowPosVal = g.NextWindowData.PosVal; 5500 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; 5501 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 5502 } 5503 else 5504 { 5505 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); 5506 } 5507 } 5508 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) 5509 { 5510 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); 5511 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); 5512 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); 5513 } 5514 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize) 5515 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; 5516 else if (first_begin_of_the_frame) 5517 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); 5518 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) 5519 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); 5520 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) 5521 FocusWindow(window); 5522 if (window->Appearing) 5523 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); 5524 5525 // When reusing window again multiple times a frame, just append content (don't need to setup again) 5526 if (first_begin_of_the_frame) 5527 { 5528 // Initialize 5529 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) 5530 window->Active = true; 5531 window->HasCloseButton = (p_open != NULL); 5532 window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); 5533 window->IDStack.resize(1); 5534 5535 // Restore buffer capacity when woken from a compacted state, to avoid 5536 if (window->MemoryCompacted) 5537 GcAwakeTransientWindowBuffers(window); 5538 5539 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). 5540 // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. 5541 bool window_title_visible_elsewhere = false; 5542 if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB 5543 window_title_visible_elsewhere = true; 5544 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) 5545 { 5546 size_t buf_len = (size_t)window->NameBufLen; 5547 window->Name = ImStrdupcpy(window->Name, &buf_len, name); 5548 window->NameBufLen = (int)buf_len; 5549 } 5550 5551 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS 5552 5553 // Update contents size from last frame for auto-fitting (or use explicit size) 5554 window->ContentSize = CalcWindowContentSize(window); 5555 if (window->HiddenFramesCanSkipItems > 0) 5556 window->HiddenFramesCanSkipItems--; 5557 if (window->HiddenFramesCannotSkipItems > 0) 5558 window->HiddenFramesCannotSkipItems--; 5559 5560 // Hide new windows for one frame until they calculate their size 5561 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) 5562 window->HiddenFramesCannotSkipItems = 1; 5563 5564 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) 5565 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. 5566 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) 5567 { 5568 window->HiddenFramesCannotSkipItems = 1; 5569 if (flags & ImGuiWindowFlags_AlwaysAutoResize) 5570 { 5571 if (!window_size_x_set_by_api) 5572 window->Size.x = window->SizeFull.x = 0.f; 5573 if (!window_size_y_set_by_api) 5574 window->Size.y = window->SizeFull.y = 0.f; 5575 window->ContentSize = ImVec2(0.f, 0.f); 5576 } 5577 } 5578 5579 // SELECT VIEWPORT 5580 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) 5581 SetCurrentWindow(window); 5582 5583 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) 5584 5585 if (flags & ImGuiWindowFlags_ChildWindow) 5586 window->WindowBorderSize = style.ChildBorderSize; 5587 else 5588 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; 5589 window->WindowPadding = style.WindowPadding; 5590 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) 5591 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); 5592 5593 // Collapse window by double-clicking on title bar 5594 // 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 5595 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) 5596 { 5597 // 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. 5598 ImRect title_bar_rect = window->TitleBarRect(); 5599 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) 5600 window->WantCollapseToggle = true; 5601 if (window->WantCollapseToggle) 5602 { 5603 window->Collapsed = !window->Collapsed; 5604 MarkIniSettingsDirty(window); 5605 FocusWindow(window); 5606 } 5607 } 5608 else 5609 { 5610 window->Collapsed = false; 5611 } 5612 window->WantCollapseToggle = false; 5613 5614 // SIZE 5615 5616 // Calculate auto-fit size, handle automatic resize 5617 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSize); 5618 bool use_current_size_for_scrollbar_x = window_just_created; 5619 bool use_current_size_for_scrollbar_y = window_just_created; 5620 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) 5621 { 5622 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. 5623 if (!window_size_x_set_by_api) 5624 { 5625 window->SizeFull.x = size_auto_fit.x; 5626 use_current_size_for_scrollbar_x = true; 5627 } 5628 if (!window_size_y_set_by_api) 5629 { 5630 window->SizeFull.y = size_auto_fit.y; 5631 use_current_size_for_scrollbar_y = true; 5632 } 5633 } 5634 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5635 { 5636 // Auto-fit may only grow window during the first few frames 5637 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. 5638 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) 5639 { 5640 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; 5641 use_current_size_for_scrollbar_x = true; 5642 } 5643 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) 5644 { 5645 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; 5646 use_current_size_for_scrollbar_y = true; 5647 } 5648 if (!window->Collapsed) 5649 MarkIniSettingsDirty(window); 5650 } 5651 5652 // Apply minimum/maximum window size constraints and final size 5653 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); 5654 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; 5655 5656 // Decoration size 5657 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 5658 5659 // POSITION 5660 5661 // Popup latch its initial position, will position itself when it appears next frame 5662 if (window_just_activated_by_user) 5663 { 5664 window->AutoPosLastDirection = ImGuiDir_None; 5665 if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) 5666 window->Pos = g.BeginPopupStack.back().OpenPopupPos; 5667 } 5668 5669 // Position child window 5670 if (flags & ImGuiWindowFlags_ChildWindow) 5671 { 5672 IM_ASSERT(parent_window && parent_window->Active); 5673 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; 5674 parent_window->DC.ChildWindows.push_back(window); 5675 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) 5676 window->Pos = parent_window->DC.CursorPos; 5677 } 5678 5679 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); 5680 if (window_pos_with_pivot) 5681 SetWindowPos(window, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) 5682 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) 5683 window->Pos = FindBestWindowPosForPopup(window); 5684 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) 5685 window->Pos = FindBestWindowPosForPopup(window); 5686 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) 5687 window->Pos = FindBestWindowPosForPopup(window); 5688 5689 // Clamp position/size so window stays visible within its viewport or monitor 5690 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. 5691 ImRect viewport_rect(GetViewportRect()); 5692 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 5693 { 5694 ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); 5695 if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) 5696 { 5697 ClampWindowRect(window, viewport_rect, clamp_padding); 5698 } 5699 } 5700 window->Pos = ImFloor(window->Pos); 5701 5702 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) 5703 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; 5704 5705 // Apply window focus (new and reactivated windows are moved to front) 5706 bool want_focus = false; 5707 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) 5708 { 5709 if (flags & ImGuiWindowFlags_Popup) 5710 want_focus = true; 5711 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) 5712 want_focus = true; 5713 } 5714 5715 // Handle manual resize: Resize Grips, Borders, Gamepad 5716 int border_held = -1; 5717 ImU32 resize_grip_col[4] = {}; 5718 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. 5719 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); 5720 if (!window->Collapsed) 5721 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0])) 5722 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; 5723 window->ResizeBorderHeld = (signed char)border_held; 5724 5725 // SCROLLBAR VISIBILITY 5726 5727 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). 5728 if (!window->Collapsed) 5729 { 5730 // When reading the current size we need to read it after size constraints have been applied. 5731 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. 5732 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); 5733 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; 5734 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; 5735 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; 5736 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; 5737 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? 5738 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); 5739 window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); 5740 if (window->ScrollbarX && !window->ScrollbarY) 5741 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); 5742 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); 5743 } 5744 5745 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) 5746 // Update various regions. Variables they depends on should be set above in this function. 5747 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. 5748 5749 // Outer rectangle 5750 // Not affected by window border size. Used by: 5751 // - FindHoveredWindow() (w/ extra padding when border resize is enabled) 5752 // - Begin() initial clipping rect for drawing window background and borders. 5753 // - Begin() clipping whole child 5754 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect; 5755 const ImRect outer_rect = window->Rect(); 5756 const ImRect title_bar_rect = window->TitleBarRect(); 5757 window->OuterRectClipped = outer_rect; 5758 window->OuterRectClipped.ClipWith(host_rect); 5759 5760 // Inner rectangle 5761 // Not affected by window border size. Used by: 5762 // - InnerClipRect 5763 // - ScrollToBringRectIntoView() 5764 // - NavUpdatePageUpPageDown() 5765 // - Scrollbar() 5766 window->InnerRect.Min.x = window->Pos.x; 5767 window->InnerRect.Min.y = window->Pos.y + decoration_up_height; 5768 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; 5769 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; 5770 5771 // Inner clipping rectangle. 5772 // Will extend a little bit outside the normal work region. 5773 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. 5774 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. 5775 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. 5776 // Affected by window/frame border size. Used by: 5777 // - Begin() initial clip rect 5778 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); 5779 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); 5780 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); 5781 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); 5782 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); 5783 window->InnerClipRect.ClipWithFull(host_rect); 5784 5785 // Default item width. Make it proportional to window size if window manually resizes 5786 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) 5787 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f); 5788 else 5789 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f); 5790 5791 // SCROLLING 5792 5793 // Lock down maximum scrolling 5794 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate 5795 // for right/bottom aligned items without creating a scrollbar. 5796 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); 5797 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); 5798 5799 // Apply scrolling 5800 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); 5801 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 5802 5803 // DRAWING 5804 5805 // Setup draw list and outer clipping rectangle 5806 window->DrawList->Clear(); 5807 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); 5808 PushClipRect(host_rect.Min, host_rect.Max, false); 5809 5810 // Draw modal window background (darkens what is behind them, all viewports) 5811 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; 5812 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); 5813 if (dim_bg_for_modal || dim_bg_for_window_list) 5814 { 5815 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); 5816 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); 5817 } 5818 5819 // Draw navigation selection/windowing rectangle background 5820 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) 5821 { 5822 ImRect bb = window->Rect(); 5823 bb.Expand(g.FontSize); 5824 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway 5825 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); 5826 } 5827 5828 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. 5829 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. 5830 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. 5831 // We also disabled this when we have dimming overlay behind this specific one child. 5832 // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. 5833 { 5834 bool render_decorations_in_parent = false; 5835 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) 5836 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) 5837 render_decorations_in_parent = true; 5838 if (render_decorations_in_parent) 5839 window->DrawList = parent_window->DrawList; 5840 5841 // Handle title bar, scrollbar, resize grips and resize borders 5842 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; 5843 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); 5844 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); 5845 5846 if (render_decorations_in_parent) 5847 window->DrawList = &window->DrawListInst; 5848 } 5849 5850 // Draw navigation selection/windowing rectangle border 5851 if (g.NavWindowingTargetAnim == window) 5852 { 5853 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); 5854 ImRect bb = window->Rect(); 5855 bb.Expand(g.FontSize); 5856 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward 5857 { 5858 bb.Expand(-g.FontSize - 1.0f); 5859 rounding = window->WindowRounding; 5860 } 5861 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); 5862 } 5863 5864 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) 5865 5866 // Work rectangle. 5867 // Affected by window padding and border size. Used by: 5868 // - Columns() for right-most edge 5869 // - TreeNode(), CollapsingHeader() for right-most edge 5870 // - BeginTabBar() for right-most edge 5871 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); 5872 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); 5873 const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); 5874 const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); 5875 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); 5876 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); 5877 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; 5878 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; 5879 5880 // [LEGACY] Content Region 5881 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. 5882 // Used by: 5883 // - Mouse wheel scrolling + many other things 5884 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; 5885 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; 5886 window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); 5887 window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); 5888 5889 // Setup drawing context 5890 // (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.) 5891 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; 5892 window->DC.GroupOffset.x = 0.0f; 5893 window->DC.ColumnsOffset.x = 0.0f; 5894 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); 5895 window->DC.CursorPos = window->DC.CursorStartPos; 5896 window->DC.CursorPosPrevLine = window->DC.CursorPos; 5897 window->DC.CursorMaxPos = window->DC.CursorStartPos; 5898 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); 5899 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; 5900 5901 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5902 window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); 5903 window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; 5904 window->DC.NavLayerActiveMaskNext = 0x00; 5905 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // -V595 5906 window->DC.NavHideHighlightOneFrame = false; 5907 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); 5908 5909 window->DC.MenuBarAppending = false; 5910 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); 5911 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; 5912 window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); 5913 window->DC.TreeDepth = 0; 5914 window->DC.TreeJumpToParentOnPopMask = 0x00; 5915 window->DC.ChildWindows.resize(0); 5916 window->DC.StateStorage = &window->StateStorage; 5917 window->DC.CurrentColumns = NULL; 5918 window->DC.LayoutType = ImGuiLayoutType_Vertical; 5919 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; 5920 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; 5921 5922 window->DC.ItemWidth = window->ItemWidthDefault; 5923 window->DC.TextWrapPos = -1.0f; // disabled 5924 window->DC.ItemFlagsStack.resize(0); 5925 window->DC.ItemWidthStack.resize(0); 5926 window->DC.TextWrapPosStack.resize(0); 5927 window->DC.GroupStack.resize(0); 5928 window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; 5929 if (parent_window) 5930 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); 5931 5932 if (window->AutoFitFramesX > 0) 5933 window->AutoFitFramesX--; 5934 if (window->AutoFitFramesY > 0) 5935 window->AutoFitFramesY--; 5936 5937 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) 5938 if (want_focus) 5939 { 5940 FocusWindow(window); 5941 NavInitWindow(window, false); 5942 } 5943 5944 // Title bar 5945 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 5946 RenderWindowTitleBarContents(window, title_bar_rect, name, p_open); 5947 5948 // Pressing CTRL+C while holding on a window copy its content to the clipboard 5949 // 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. 5950 // Maybe we can support CTRL+C on every element? 5951 /* 5952 if (g.ActiveId == move_id) 5953 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) 5954 LogToClipboard(); 5955 */ 5956 5957 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). 5958 // This is useful to allow creating context menus on title bar only, etc. 5959 window->DC.LastItemId = window->MoveId; 5960 window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; 5961 window->DC.LastItemRect = title_bar_rect; 5962#ifdef IMGUI_ENABLE_TEST_ENGINE 5963 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5964 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); 5965#endif 5966 } 5967 else 5968 { 5969 // Append 5970 SetCurrentWindow(window); 5971 } 5972 5973 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); 5974 5975 // 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) 5976 if (first_begin_of_the_frame) 5977 window->WriteAccessed = false; 5978 5979 window->BeginCount++; 5980 g.NextWindowData.ClearFlags(); 5981 5982 if (flags & ImGuiWindowFlags_ChildWindow) 5983 { 5984 // Child window can be out of sight and have "negative" clip windows. 5985 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). 5986 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); 5987 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 5988 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) 5989 window->HiddenFramesCanSkipItems = 1; 5990 5991 // Hide along with parent or if parent is collapsed 5992 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) 5993 window->HiddenFramesCanSkipItems = 1; 5994 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) 5995 window->HiddenFramesCannotSkipItems = 1; 5996 } 5997 5998 // 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) 5999 if (style.Alpha <= 0.0f) 6000 window->HiddenFramesCanSkipItems = 1; 6001 6002 // Update the Hidden flag 6003 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); 6004 6005 // Update the SkipItems flag, used to early out of all items functions (no layout required) 6006 bool skip_items = false; 6007 if (window->Collapsed || !window->Active || window->Hidden) 6008 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) 6009 skip_items = true; 6010 window->SkipItems = skip_items; 6011 6012 return !skip_items; 6013} 6014 6015void ImGui::End() 6016{ 6017 ImGuiContext& g = *GImGui; 6018 ImGuiWindow* window = g.CurrentWindow; 6019 6020 // Error checking: verify that user hasn't called End() too many times! 6021 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow) 6022 { 6023 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!"); 6024 return; 6025 } 6026 IM_ASSERT(g.CurrentWindowStack.Size > 0); 6027 6028 // Error checking: verify that user doesn't directly call End() on a child window. 6029 if (window->Flags & ImGuiWindowFlags_ChildWindow) 6030 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); 6031 6032 // Close anything that is open 6033 if (window->DC.CurrentColumns) 6034 EndColumns(); 6035 PopClipRect(); // Inner window clip rectangle 6036 6037 // Stop logging 6038 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging 6039 LogFinish(); 6040 6041 // Pop from window stack 6042 g.CurrentWindowStack.pop_back(); 6043 if (window->Flags & ImGuiWindowFlags_Popup) 6044 g.BeginPopupStack.pop_back(); 6045 ErrorCheckBeginEndCompareStacksSize(window, false); 6046 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); 6047} 6048 6049void ImGui::BringWindowToFocusFront(ImGuiWindow* window) 6050{ 6051 ImGuiContext& g = *GImGui; 6052 if (g.WindowsFocusOrder.back() == window) 6053 return; 6054 for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the top-most window 6055 if (g.WindowsFocusOrder[i] == window) 6056 { 6057 memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); 6058 g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; 6059 break; 6060 } 6061} 6062 6063void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) 6064{ 6065 ImGuiContext& g = *GImGui; 6066 ImGuiWindow* current_front_window = g.Windows.back(); 6067 if (current_front_window == window || current_front_window->RootWindow == window) 6068 return; 6069 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window 6070 if (g.Windows[i] == window) 6071 { 6072 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); 6073 g.Windows[g.Windows.Size - 1] = window; 6074 break; 6075 } 6076} 6077 6078void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) 6079{ 6080 ImGuiContext& g = *GImGui; 6081 if (g.Windows[0] == window) 6082 return; 6083 for (int i = 0; i < g.Windows.Size; i++) 6084 if (g.Windows[i] == window) 6085 { 6086 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); 6087 g.Windows[0] = window; 6088 break; 6089 } 6090} 6091 6092// Moving window to front of display and set focus (which happens to be back of our sorted list) 6093void ImGui::FocusWindow(ImGuiWindow* window) 6094{ 6095 ImGuiContext& g = *GImGui; 6096 6097 if (g.NavWindow != window) 6098 { 6099 g.NavWindow = window; 6100 if (window && g.NavDisableMouseHover) 6101 g.NavMousePosDirty = true; 6102 g.NavInitRequest = false; 6103 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId 6104 g.NavFocusScopeId = 0; 6105 g.NavIdIsAlive = false; 6106 g.NavLayer = ImGuiNavLayer_Main; 6107 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); 6108 } 6109 6110 // Close popups if any 6111 ClosePopupsOverWindow(window, false); 6112 6113 // Passing NULL allow to disable keyboard focus 6114 if (!window) 6115 return; 6116 6117 // Move the root window to the top of the pile 6118 IM_ASSERT(window->RootWindow != NULL); 6119 ImGuiWindow* focus_front_window = window->RootWindow; // NB: In docking branch this is window->RootWindowDockStop 6120 ImGuiWindow* display_front_window = window->RootWindow; 6121 6122 // Steal focus on active widgets 6123 if (focus_front_window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement may be unnecessary? Need further testing before removing it.. 6124 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) 6125 ClearActiveID(); 6126 6127 // Bring to front 6128 BringWindowToFocusFront(focus_front_window); 6129 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) 6130 BringWindowToDisplayFront(display_front_window); 6131} 6132 6133void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) 6134{ 6135 ImGuiContext& g = *GImGui; 6136 6137 int start_idx = g.WindowsFocusOrder.Size - 1; 6138 if (under_this_window != NULL) 6139 { 6140 int under_this_window_idx = FindWindowFocusIndex(under_this_window); 6141 if (under_this_window_idx != -1) 6142 start_idx = under_this_window_idx - 1; 6143 } 6144 for (int i = start_idx; i >= 0; i--) 6145 { 6146 // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. 6147 ImGuiWindow* window = g.WindowsFocusOrder[i]; 6148 if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) 6149 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) 6150 { 6151 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); 6152 FocusWindow(focus_window); 6153 return; 6154 } 6155 } 6156 FocusWindow(NULL); 6157} 6158 6159void ImGui::SetCurrentFont(ImFont* font) 6160{ 6161 ImGuiContext& g = *GImGui; 6162 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? 6163 IM_ASSERT(font->Scale > 0.0f); 6164 g.Font = font; 6165 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); 6166 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; 6167 6168 ImFontAtlas* atlas = g.Font->ContainerAtlas; 6169 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; 6170 g.DrawListSharedData.Font = g.Font; 6171 g.DrawListSharedData.FontSize = g.FontSize; 6172} 6173 6174void ImGui::PushFont(ImFont* font) 6175{ 6176 ImGuiContext& g = *GImGui; 6177 if (!font) 6178 font = GetDefaultFont(); 6179 SetCurrentFont(font); 6180 g.FontStack.push_back(font); 6181 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); 6182} 6183 6184void ImGui::PopFont() 6185{ 6186 ImGuiContext& g = *GImGui; 6187 g.CurrentWindow->DrawList->PopTextureID(); 6188 g.FontStack.pop_back(); 6189 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); 6190} 6191 6192void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) 6193{ 6194 ImGuiWindow* window = GetCurrentWindow(); 6195 if (enabled) 6196 window->DC.ItemFlags |= option; 6197 else 6198 window->DC.ItemFlags &= ~option; 6199 window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); 6200} 6201 6202void ImGui::PopItemFlag() 6203{ 6204 ImGuiWindow* window = GetCurrentWindow(); 6205 window->DC.ItemFlagsStack.pop_back(); 6206 window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); 6207} 6208 6209// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. 6210void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) 6211{ 6212 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus); 6213} 6214 6215void ImGui::PopAllowKeyboardFocus() 6216{ 6217 PopItemFlag(); 6218} 6219 6220void ImGui::PushButtonRepeat(bool repeat) 6221{ 6222 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); 6223} 6224 6225void ImGui::PopButtonRepeat() 6226{ 6227 PopItemFlag(); 6228} 6229 6230void ImGui::PushTextWrapPos(float wrap_pos_x) 6231{ 6232 ImGuiWindow* window = GetCurrentWindow(); 6233 window->DC.TextWrapPos = wrap_pos_x; 6234 window->DC.TextWrapPosStack.push_back(wrap_pos_x); 6235} 6236 6237void ImGui::PopTextWrapPos() 6238{ 6239 ImGuiWindow* window = GetCurrentWindow(); 6240 window->DC.TextWrapPosStack.pop_back(); 6241 window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); 6242} 6243 6244bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) 6245{ 6246 if (window->RootWindow == potential_parent) 6247 return true; 6248 while (window != NULL) 6249 { 6250 if (window == potential_parent) 6251 return true; 6252 window = window->ParentWindow; 6253 } 6254 return false; 6255} 6256 6257bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) 6258{ 6259 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function 6260 ImGuiContext& g = *GImGui; 6261 6262 if (flags & ImGuiHoveredFlags_AnyWindow) 6263 { 6264 if (g.HoveredWindow == NULL) 6265 return false; 6266 } 6267 else 6268 { 6269 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) 6270 { 6271 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: 6272 if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) 6273 return false; 6274 break; 6275 case ImGuiHoveredFlags_RootWindow: 6276 if (g.HoveredWindow != g.CurrentWindow->RootWindow) 6277 return false; 6278 break; 6279 case ImGuiHoveredFlags_ChildWindows: 6280 if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow)) 6281 return false; 6282 break; 6283 default: 6284 if (g.HoveredWindow != g.CurrentWindow) 6285 return false; 6286 break; 6287 } 6288 } 6289 6290 if (!IsWindowContentHoverable(g.HoveredWindow, flags)) 6291 return false; 6292 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 6293 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) 6294 return false; 6295 return true; 6296} 6297 6298bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) 6299{ 6300 ImGuiContext& g = *GImGui; 6301 6302 if (flags & ImGuiFocusedFlags_AnyWindow) 6303 return g.NavWindow != NULL; 6304 6305 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() 6306 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) 6307 { 6308 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: 6309 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; 6310 case ImGuiFocusedFlags_RootWindow: 6311 return g.NavWindow == g.CurrentWindow->RootWindow; 6312 case ImGuiFocusedFlags_ChildWindows: 6313 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); 6314 default: 6315 return g.NavWindow == g.CurrentWindow; 6316 } 6317} 6318 6319// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) 6320// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly. 6321// If you want a window to never be focused, you may use the e.g. NoInputs flag. 6322bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) 6323{ 6324 return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); 6325} 6326 6327float ImGui::GetWindowWidth() 6328{ 6329 ImGuiWindow* window = GImGui->CurrentWindow; 6330 return window->Size.x; 6331} 6332 6333float ImGui::GetWindowHeight() 6334{ 6335 ImGuiWindow* window = GImGui->CurrentWindow; 6336 return window->Size.y; 6337} 6338 6339ImVec2 ImGui::GetWindowPos() 6340{ 6341 ImGuiContext& g = *GImGui; 6342 ImGuiWindow* window = g.CurrentWindow; 6343 return window->Pos; 6344} 6345 6346void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) 6347{ 6348 // Test condition (NB: bit 0 is always true) and clear flags for next time 6349 if (cond && (window->SetWindowPosAllowFlags & cond) == 0) 6350 return; 6351 6352 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6353 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6354 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); 6355 6356 // Set 6357 const ImVec2 old_pos = window->Pos; 6358 window->Pos = ImFloor(pos); 6359 ImVec2 offset = window->Pos - old_pos; 6360 window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor 6361 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. 6362 window->DC.CursorStartPos += offset; 6363} 6364 6365void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) 6366{ 6367 ImGuiWindow* window = GetCurrentWindowRead(); 6368 SetWindowPos(window, pos, cond); 6369} 6370 6371void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond) 6372{ 6373 if (ImGuiWindow* window = FindWindowByName(name)) 6374 SetWindowPos(window, pos, cond); 6375} 6376 6377ImVec2 ImGui::GetWindowSize() 6378{ 6379 ImGuiWindow* window = GetCurrentWindowRead(); 6380 return window->Size; 6381} 6382 6383void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) 6384{ 6385 // Test condition (NB: bit 0 is always true) and clear flags for next time 6386 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) 6387 return; 6388 6389 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6390 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6391 6392 // Set 6393 if (size.x > 0.0f) 6394 { 6395 window->AutoFitFramesX = 0; 6396 window->SizeFull.x = IM_FLOOR(size.x); 6397 } 6398 else 6399 { 6400 window->AutoFitFramesX = 2; 6401 window->AutoFitOnlyGrows = false; 6402 } 6403 if (size.y > 0.0f) 6404 { 6405 window->AutoFitFramesY = 0; 6406 window->SizeFull.y = IM_FLOOR(size.y); 6407 } 6408 else 6409 { 6410 window->AutoFitFramesY = 2; 6411 window->AutoFitOnlyGrows = false; 6412 } 6413} 6414 6415void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) 6416{ 6417 SetWindowSize(GImGui->CurrentWindow, size, cond); 6418} 6419 6420void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) 6421{ 6422 if (ImGuiWindow* window = FindWindowByName(name)) 6423 SetWindowSize(window, size, cond); 6424} 6425 6426void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) 6427{ 6428 // Test condition (NB: bit 0 is always true) and clear flags for next time 6429 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) 6430 return; 6431 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6432 6433 // Set 6434 window->Collapsed = collapsed; 6435} 6436 6437void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) 6438{ 6439 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); 6440} 6441 6442bool ImGui::IsWindowCollapsed() 6443{ 6444 ImGuiWindow* window = GetCurrentWindowRead(); 6445 return window->Collapsed; 6446} 6447 6448bool ImGui::IsWindowAppearing() 6449{ 6450 ImGuiWindow* window = GetCurrentWindowRead(); 6451 return window->Appearing; 6452} 6453 6454void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) 6455{ 6456 if (ImGuiWindow* window = FindWindowByName(name)) 6457 SetWindowCollapsed(window, collapsed, cond); 6458} 6459 6460void ImGui::SetWindowFocus() 6461{ 6462 FocusWindow(GImGui->CurrentWindow); 6463} 6464 6465void ImGui::SetWindowFocus(const char* name) 6466{ 6467 if (name) 6468 { 6469 if (ImGuiWindow* window = FindWindowByName(name)) 6470 FocusWindow(window); 6471 } 6472 else 6473 { 6474 FocusWindow(NULL); 6475 } 6476} 6477 6478void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) 6479{ 6480 ImGuiContext& g = *GImGui; 6481 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6482 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos; 6483 g.NextWindowData.PosVal = pos; 6484 g.NextWindowData.PosPivotVal = pivot; 6485 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; 6486} 6487 6488void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) 6489{ 6490 ImGuiContext& g = *GImGui; 6491 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6492 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize; 6493 g.NextWindowData.SizeVal = size; 6494 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; 6495} 6496 6497void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) 6498{ 6499 ImGuiContext& g = *GImGui; 6500 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; 6501 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); 6502 g.NextWindowData.SizeCallback = custom_callback; 6503 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; 6504} 6505 6506// Content size = inner scrollable rectangle, padded with WindowPadding. 6507// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. 6508void ImGui::SetNextWindowContentSize(const ImVec2& size) 6509{ 6510 ImGuiContext& g = *GImGui; 6511 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize; 6512 g.NextWindowData.ContentSizeVal = size; 6513} 6514 6515void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) 6516{ 6517 ImGuiContext& g = *GImGui; 6518 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6519 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed; 6520 g.NextWindowData.CollapsedVal = collapsed; 6521 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; 6522} 6523 6524void ImGui::SetNextWindowFocus() 6525{ 6526 ImGuiContext& g = *GImGui; 6527 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus; 6528} 6529 6530void ImGui::SetNextWindowBgAlpha(float alpha) 6531{ 6532 ImGuiContext& g = *GImGui; 6533 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha; 6534 g.NextWindowData.BgAlphaVal = alpha; 6535} 6536 6537ImDrawList* ImGui::GetWindowDrawList() 6538{ 6539 ImGuiWindow* window = GetCurrentWindow(); 6540 return window->DrawList; 6541} 6542 6543ImFont* ImGui::GetFont() 6544{ 6545 return GImGui->Font; 6546} 6547 6548float ImGui::GetFontSize() 6549{ 6550 return GImGui->FontSize; 6551} 6552 6553ImVec2 ImGui::GetFontTexUvWhitePixel() 6554{ 6555 return GImGui->DrawListSharedData.TexUvWhitePixel; 6556} 6557 6558void ImGui::SetWindowFontScale(float scale) 6559{ 6560 ImGuiContext& g = *GImGui; 6561 ImGuiWindow* window = GetCurrentWindow(); 6562 window->FontWindowScale = scale; 6563 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 6564} 6565 6566void ImGui::ActivateItem(ImGuiID id) 6567{ 6568 ImGuiContext& g = *GImGui; 6569 g.NavNextActivateId = id; 6570} 6571 6572void ImGui::PushFocusScope(ImGuiID id) 6573{ 6574 ImGuiContext& g = *GImGui; 6575 ImGuiWindow* window = g.CurrentWindow; 6576 window->IDStack.push_back(window->DC.NavFocusScopeIdCurrent); 6577 window->DC.NavFocusScopeIdCurrent = id; 6578} 6579 6580void ImGui::PopFocusScope() 6581{ 6582 ImGuiContext& g = *GImGui; 6583 ImGuiWindow* window = g.CurrentWindow; 6584 window->DC.NavFocusScopeIdCurrent = window->IDStack.back(); 6585 window->IDStack.pop_back(); 6586} 6587 6588void ImGui::SetKeyboardFocusHere(int offset) 6589{ 6590 IM_ASSERT(offset >= -1); // -1 is allowed but not below 6591 ImGuiContext& g = *GImGui; 6592 ImGuiWindow* window = g.CurrentWindow; 6593 g.FocusRequestNextWindow = window; 6594 g.FocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; 6595 g.FocusRequestNextCounterTabStop = INT_MAX; 6596} 6597 6598void ImGui::SetItemDefaultFocus() 6599{ 6600 ImGuiContext& g = *GImGui; 6601 ImGuiWindow* window = g.CurrentWindow; 6602 if (!window->Appearing) 6603 return; 6604 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) 6605 { 6606 g.NavInitRequest = false; 6607 g.NavInitResultId = g.NavWindow->DC.LastItemId; 6608 g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); 6609 NavUpdateAnyRequestFlag(); 6610 if (!IsItemVisible()) 6611 SetScrollHereY(); 6612 } 6613} 6614 6615void ImGui::SetStateStorage(ImGuiStorage* tree) 6616{ 6617 ImGuiWindow* window = GImGui->CurrentWindow; 6618 window->DC.StateStorage = tree ? tree : &window->StateStorage; 6619} 6620 6621ImGuiStorage* ImGui::GetStateStorage() 6622{ 6623 ImGuiWindow* window = GImGui->CurrentWindow; 6624 return window->DC.StateStorage; 6625} 6626 6627void ImGui::PushID(const char* str_id) 6628{ 6629 ImGuiWindow* window = GImGui->CurrentWindow; 6630 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id)); 6631} 6632 6633void ImGui::PushID(const char* str_id_begin, const char* str_id_end) 6634{ 6635 ImGuiWindow* window = GImGui->CurrentWindow; 6636 window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end)); 6637} 6638 6639void ImGui::PushID(const void* ptr_id) 6640{ 6641 ImGuiWindow* window = GImGui->CurrentWindow; 6642 window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id)); 6643} 6644 6645void ImGui::PushID(int int_id) 6646{ 6647 ImGuiWindow* window = GImGui->CurrentWindow; 6648 window->IDStack.push_back(window->GetIDNoKeepAlive(int_id)); 6649} 6650 6651// Push a given id value ignoring the ID stack as a seed. 6652void ImGui::PushOverrideID(ImGuiID id) 6653{ 6654 ImGuiWindow* window = GImGui->CurrentWindow; 6655 window->IDStack.push_back(id); 6656} 6657 6658void ImGui::PopID() 6659{ 6660 ImGuiWindow* window = GImGui->CurrentWindow; 6661 window->IDStack.pop_back(); 6662} 6663 6664ImGuiID ImGui::GetID(const char* str_id) 6665{ 6666 ImGuiWindow* window = GImGui->CurrentWindow; 6667 return window->GetID(str_id); 6668} 6669 6670ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) 6671{ 6672 ImGuiWindow* window = GImGui->CurrentWindow; 6673 return window->GetID(str_id_begin, str_id_end); 6674} 6675 6676ImGuiID ImGui::GetID(const void* ptr_id) 6677{ 6678 ImGuiWindow* window = GImGui->CurrentWindow; 6679 return window->GetID(ptr_id); 6680} 6681 6682bool ImGui::IsRectVisible(const ImVec2& size) 6683{ 6684 ImGuiWindow* window = GImGui->CurrentWindow; 6685 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); 6686} 6687 6688bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) 6689{ 6690 ImGuiWindow* window = GImGui->CurrentWindow; 6691 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); 6692} 6693 6694 6695//----------------------------------------------------------------------------- 6696// [SECTION] ERROR CHECKING 6697//----------------------------------------------------------------------------- 6698 6699// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. 6700// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit 6701// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code 6702// may see different structures than what imgui.cpp sees, which is problematic. 6703// We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. 6704bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) 6705{ 6706 bool error = false; 6707 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } 6708 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } 6709 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } 6710 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } 6711 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } 6712 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } 6713 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } 6714 return !error; 6715} 6716 6717static void ImGui::ErrorCheckNewFrameSanityChecks() 6718{ 6719 ImGuiContext& g = *GImGui; 6720 6721 // Check user IM_ASSERT macro 6722 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined! 6723 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. 6724 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) 6725 // #define IM_ASSERT(EXPR) SomeCode(EXPR); SomeMoreCode(); // Wrong! 6726 // #define IM_ASSERT(EXPR) do { SomeCode(EXPR); SomeMoreCode(); } while (0) // Correct! 6727 if (true) IM_ASSERT(1); else IM_ASSERT(0); 6728 6729 // Check user data 6730 // (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) 6731 IM_ASSERT(g.Initialized); 6732 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); 6733 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); 6734 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); 6735 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); 6736 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); 6737 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); 6738 IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!"); 6739 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)!"); 6740 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); 6741 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); 6742 for (int n = 0; n < ImGuiKey_COUNT; n++) 6743 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)"); 6744 6745 // 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) 6746 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) 6747 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); 6748 6749 // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. 6750 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) 6751 g.IO.ConfigWindowsResizeFromEdges = false; 6752} 6753 6754static void ImGui::ErrorCheckEndFrameSanityChecks() 6755{ 6756 ImGuiContext& g = *GImGui; 6757 6758 // Verify that io.KeyXXX fields haven't been tampered with. Key mods shoudl not be modified between NewFrame() and EndFrame() 6759 const ImGuiKeyModFlags expected_key_mod_flags = GetMergedKeyModFlags(); 6760 IM_ASSERT(g.IO.KeyMods == expected_key_mod_flags && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); 6761 IM_UNUSED(expected_key_mod_flags); 6762 6763 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you 6764 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). 6765 if (g.CurrentWindowStack.Size != 1) 6766 { 6767 if (g.CurrentWindowStack.Size > 1) 6768 { 6769 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); 6770 while (g.CurrentWindowStack.Size > 1) 6771 End(); 6772 } 6773 else 6774 { 6775 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); 6776 } 6777 } 6778} 6779 6780// Save and compare stack sizes on Begin()/End() to detect usage errors 6781// Begin() calls this with write=true 6782// End() calls this with write=false 6783static void ImGui::ErrorCheckBeginEndCompareStacksSize(ImGuiWindow* window, bool write) 6784{ 6785 ImGuiContext& g = *GImGui; 6786 short* p = &window->DC.StackSizesBackup[0]; 6787 6788 // Window stacks 6789 // 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) 6790 { int n = window->IDStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "PushID/PopID or TreeNode/TreePop Mismatch!"); p++; } // Too few or too many PopID()/TreePop() 6791 { int n = window->DC.GroupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginGroup/EndGroup Mismatch!"); p++; } // Too few or too many EndGroup() 6792 6793 // Global stacks 6794 // 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. 6795 { int n = g.BeginPopupStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p == n && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch!"); p++; }// Too few or too many EndMenu()/EndPopup() 6796 { int n = g.ColorModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleColor/PopStyleColor Mismatch!"); p++; } // Too few or too many PopStyleColor() 6797 { int n = g.StyleModifiers.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushStyleVar/PopStyleVar Mismatch!"); p++; } // Too few or too many PopStyleVar() 6798 { int n = g.FontStack.Size; if (write) *p = (short)n; else IM_ASSERT(*p >= n && "PushFont/PopFont Mismatch!"); p++; } // Too few or too many PopFont() 6799 IM_ASSERT(p == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); 6800} 6801 6802 6803//----------------------------------------------------------------------------- 6804// [SECTION] LAYOUT 6805//----------------------------------------------------------------------------- 6806// - ItemSize() 6807// - ItemAdd() 6808// - SameLine() 6809// - GetCursorScreenPos() 6810// - SetCursorScreenPos() 6811// - GetCursorPos(), GetCursorPosX(), GetCursorPosY() 6812// - SetCursorPos(), SetCursorPosX(), SetCursorPosY() 6813// - GetCursorStartPos() 6814// - Indent() 6815// - Unindent() 6816// - SetNextItemWidth() 6817// - PushItemWidth() 6818// - PushMultiItemsWidths() 6819// - PopItemWidth() 6820// - CalcItemWidth() 6821// - CalcItemSize() 6822// - GetTextLineHeight() 6823// - GetTextLineHeightWithSpacing() 6824// - GetFrameHeight() 6825// - GetFrameHeightWithSpacing() 6826// - GetContentRegionMax() 6827// - GetContentRegionMaxAbs() [Internal] 6828// - GetContentRegionAvail(), 6829// - GetWindowContentRegionMin(), GetWindowContentRegionMax() 6830// - GetWindowContentRegionWidth() 6831// - BeginGroup() 6832// - EndGroup() 6833// Also see in imgui_widgets: tab bars, columns. 6834//----------------------------------------------------------------------------- 6835 6836// Advance cursor given item size for layout. 6837// Register minimum needed size so it can extend the bounding box used for auto-fit calculation. 6838// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. 6839void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) 6840{ 6841 ImGuiContext& g = *GImGui; 6842 ImGuiWindow* window = g.CurrentWindow; 6843 if (window->SkipItems) 6844 return; 6845 6846 // We increase the height in this function to accommodate for baseline offset. 6847 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor, 6848 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect. 6849 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f; 6850 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y); 6851 6852 // Always align ourselves on pixel boundaries 6853 //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] 6854 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; 6855 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y; 6856 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line 6857 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line 6858 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); 6859 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); 6860 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] 6861 6862 window->DC.PrevLineSize.y = line_height; 6863 window->DC.CurrLineSize.y = 0.0f; 6864 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); 6865 window->DC.CurrLineTextBaseOffset = 0.0f; 6866 6867 // Horizontal layout mode 6868 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) 6869 SameLine(); 6870} 6871 6872void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) 6873{ 6874 ItemSize(bb.GetSize(), text_baseline_y); 6875} 6876 6877// Declare item bounding box for clipping and interaction. 6878// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface 6879// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. 6880bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) 6881{ 6882 ImGuiContext& g = *GImGui; 6883 ImGuiWindow* window = g.CurrentWindow; 6884 6885 if (id != 0) 6886 { 6887 // Navigation processing runs prior to clipping early-out 6888 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget 6889 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests 6890 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of 6891 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. 6892 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able 6893 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). 6894 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. 6895 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. 6896 window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; 6897 if (g.NavId == id || g.NavAnyRequest) 6898 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) 6899 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) 6900 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); 6901 6902 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() 6903#ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 6904 if (id == g.DebugItemPickerBreakId) 6905 { 6906 IM_DEBUG_BREAK(); 6907 g.DebugItemPickerBreakId = 0; 6908 } 6909#endif 6910 } 6911 6912 window->DC.LastItemId = id; 6913 window->DC.LastItemRect = bb; 6914 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; 6915 g.NextItemData.Flags = ImGuiNextItemDataFlags_None; 6916 6917#ifdef IMGUI_ENABLE_TEST_ENGINE 6918 if (id != 0) 6919 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id); 6920#endif 6921 6922 // Clipping test 6923 const bool is_clipped = IsClippedEx(bb, id, false); 6924 if (is_clipped) 6925 return false; 6926 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] 6927 6928 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) 6929 if (IsMouseHoveringRect(bb.Min, bb.Max)) 6930 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; 6931 return true; 6932} 6933 6934// Gets back to previous line and continue with horizontal layout 6935// offset_from_start_x == 0 : follow right after previous item 6936// offset_from_start_x != 0 : align to specified x position (relative to window/group left) 6937// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 6938// spacing_w >= 0 : enforce spacing amount 6939void ImGui::SameLine(float offset_from_start_x, float spacing_w) 6940{ 6941 ImGuiWindow* window = GetCurrentWindow(); 6942 if (window->SkipItems) 6943 return; 6944 6945 ImGuiContext& g = *GImGui; 6946 if (offset_from_start_x != 0.0f) 6947 { 6948 if (spacing_w < 0.0f) spacing_w = 0.0f; 6949 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x; 6950 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 6951 } 6952 else 6953 { 6954 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; 6955 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; 6956 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 6957 } 6958 window->DC.CurrLineSize = window->DC.PrevLineSize; 6959 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; 6960} 6961 6962ImVec2 ImGui::GetCursorScreenPos() 6963{ 6964 ImGuiWindow* window = GetCurrentWindowRead(); 6965 return window->DC.CursorPos; 6966} 6967 6968void ImGui::SetCursorScreenPos(const ImVec2& pos) 6969{ 6970 ImGuiWindow* window = GetCurrentWindow(); 6971 window->DC.CursorPos = pos; 6972 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 6973} 6974 6975// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. 6976// 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'. 6977ImVec2 ImGui::GetCursorPos() 6978{ 6979 ImGuiWindow* window = GetCurrentWindowRead(); 6980 return window->DC.CursorPos - window->Pos + window->Scroll; 6981} 6982 6983float ImGui::GetCursorPosX() 6984{ 6985 ImGuiWindow* window = GetCurrentWindowRead(); 6986 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; 6987} 6988 6989float ImGui::GetCursorPosY() 6990{ 6991 ImGuiWindow* window = GetCurrentWindowRead(); 6992 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; 6993} 6994 6995void ImGui::SetCursorPos(const ImVec2& local_pos) 6996{ 6997 ImGuiWindow* window = GetCurrentWindow(); 6998 window->DC.CursorPos = window->Pos - window->Scroll + local_pos; 6999 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 7000} 7001 7002void ImGui::SetCursorPosX(float x) 7003{ 7004 ImGuiWindow* window = GetCurrentWindow(); 7005 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; 7006 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); 7007} 7008 7009void ImGui::SetCursorPosY(float y) 7010{ 7011 ImGuiWindow* window = GetCurrentWindow(); 7012 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; 7013 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); 7014} 7015 7016ImVec2 ImGui::GetCursorStartPos() 7017{ 7018 ImGuiWindow* window = GetCurrentWindowRead(); 7019 return window->DC.CursorStartPos - window->Pos; 7020} 7021 7022void ImGui::Indent(float indent_w) 7023{ 7024 ImGuiContext& g = *GImGui; 7025 ImGuiWindow* window = GetCurrentWindow(); 7026 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 7027 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; 7028} 7029 7030void ImGui::Unindent(float indent_w) 7031{ 7032 ImGuiContext& g = *GImGui; 7033 ImGuiWindow* window = GetCurrentWindow(); 7034 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 7035 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; 7036} 7037 7038// Affect large frame+labels widgets only. 7039void ImGui::SetNextItemWidth(float item_width) 7040{ 7041 ImGuiContext& g = *GImGui; 7042 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth; 7043 g.NextItemData.Width = item_width; 7044} 7045 7046void ImGui::PushItemWidth(float item_width) 7047{ 7048 ImGuiContext& g = *GImGui; 7049 ImGuiWindow* window = g.CurrentWindow; 7050 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); 7051 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); 7052 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; 7053} 7054 7055void ImGui::PushMultiItemsWidths(int components, float w_full) 7056{ 7057 ImGuiContext& g = *GImGui; 7058 ImGuiWindow* window = g.CurrentWindow; 7059 const ImGuiStyle& style = g.Style; 7060 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); 7061 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); 7062 window->DC.ItemWidthStack.push_back(w_item_last); 7063 for (int i = 0; i < components-1; i++) 7064 window->DC.ItemWidthStack.push_back(w_item_one); 7065 window->DC.ItemWidth = window->DC.ItemWidthStack.back(); 7066 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; 7067} 7068 7069void ImGui::PopItemWidth() 7070{ 7071 ImGuiWindow* window = GetCurrentWindow(); 7072 window->DC.ItemWidthStack.pop_back(); 7073 window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); 7074} 7075 7076// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). 7077// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags() 7078float ImGui::CalcItemWidth() 7079{ 7080 ImGuiContext& g = *GImGui; 7081 ImGuiWindow* window = g.CurrentWindow; 7082 float w; 7083 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) 7084 w = g.NextItemData.Width; 7085 else 7086 w = window->DC.ItemWidth; 7087 if (w < 0.0f) 7088 { 7089 float region_max_x = GetContentRegionMaxAbs().x; 7090 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); 7091 } 7092 w = IM_FLOOR(w); 7093 return w; 7094} 7095 7096// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). 7097// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. 7098// Note that only CalcItemWidth() is publicly exposed. 7099// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) 7100ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) 7101{ 7102 ImGuiWindow* window = GImGui->CurrentWindow; 7103 7104 ImVec2 region_max; 7105 if (size.x < 0.0f || size.y < 0.0f) 7106 region_max = GetContentRegionMaxAbs(); 7107 7108 if (size.x == 0.0f) 7109 size.x = default_w; 7110 else if (size.x < 0.0f) 7111 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x); 7112 7113 if (size.y == 0.0f) 7114 size.y = default_h; 7115 else if (size.y < 0.0f) 7116 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y); 7117 7118 return size; 7119} 7120 7121float ImGui::GetTextLineHeight() 7122{ 7123 ImGuiContext& g = *GImGui; 7124 return g.FontSize; 7125} 7126 7127float ImGui::GetTextLineHeightWithSpacing() 7128{ 7129 ImGuiContext& g = *GImGui; 7130 return g.FontSize + g.Style.ItemSpacing.y; 7131} 7132 7133float ImGui::GetFrameHeight() 7134{ 7135 ImGuiContext& g = *GImGui; 7136 return g.FontSize + g.Style.FramePadding.y * 2.0f; 7137} 7138 7139float ImGui::GetFrameHeightWithSpacing() 7140{ 7141 ImGuiContext& g = *GImGui; 7142 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; 7143} 7144 7145// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW "WORK RECT" API. Thanks for your patience! 7146 7147// FIXME: This is in window space (not screen space!). 7148ImVec2 ImGui::GetContentRegionMax() 7149{ 7150 ImGuiContext& g = *GImGui; 7151 ImGuiWindow* window = g.CurrentWindow; 7152 ImVec2 mx = window->ContentRegionRect.Max - window->Pos; 7153 if (window->DC.CurrentColumns) 7154 mx.x = window->WorkRect.Max.x - window->Pos.x; 7155 return mx; 7156} 7157 7158// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. 7159ImVec2 ImGui::GetContentRegionMaxAbs() 7160{ 7161 ImGuiContext& g = *GImGui; 7162 ImGuiWindow* window = g.CurrentWindow; 7163 ImVec2 mx = window->ContentRegionRect.Max; 7164 if (window->DC.CurrentColumns) 7165 mx.x = window->WorkRect.Max.x; 7166 return mx; 7167} 7168 7169ImVec2 ImGui::GetContentRegionAvail() 7170{ 7171 ImGuiWindow* window = GImGui->CurrentWindow; 7172 return GetContentRegionMaxAbs() - window->DC.CursorPos; 7173} 7174 7175// In window space (not screen space!) 7176ImVec2 ImGui::GetWindowContentRegionMin() 7177{ 7178 ImGuiWindow* window = GImGui->CurrentWindow; 7179 return window->ContentRegionRect.Min - window->Pos; 7180} 7181 7182ImVec2 ImGui::GetWindowContentRegionMax() 7183{ 7184 ImGuiWindow* window = GImGui->CurrentWindow; 7185 return window->ContentRegionRect.Max - window->Pos; 7186} 7187 7188float ImGui::GetWindowContentRegionWidth() 7189{ 7190 ImGuiWindow* window = GImGui->CurrentWindow; 7191 return window->ContentRegionRect.GetWidth(); 7192} 7193 7194// 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.) 7195void ImGui::BeginGroup() 7196{ 7197 ImGuiContext& g = *GImGui; 7198 ImGuiWindow* window = GetCurrentWindow(); 7199 7200 window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); 7201 ImGuiGroupData& group_data = window->DC.GroupStack.back(); 7202 group_data.BackupCursorPos = window->DC.CursorPos; 7203 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; 7204 group_data.BackupIndent = window->DC.Indent; 7205 group_data.BackupGroupOffset = window->DC.GroupOffset; 7206 group_data.BackupCurrLineSize = window->DC.CurrLineSize; 7207 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; 7208 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; 7209 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; 7210 group_data.EmitItem = true; 7211 7212 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x; 7213 window->DC.Indent = window->DC.GroupOffset; 7214 window->DC.CursorMaxPos = window->DC.CursorPos; 7215 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); 7216 if (g.LogEnabled) 7217 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return 7218} 7219 7220void ImGui::EndGroup() 7221{ 7222 ImGuiContext& g = *GImGui; 7223 ImGuiWindow* window = GetCurrentWindow(); 7224 IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls 7225 7226 ImGuiGroupData& group_data = window->DC.GroupStack.back(); 7227 7228 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); 7229 7230 window->DC.CursorPos = group_data.BackupCursorPos; 7231 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); 7232 window->DC.Indent = group_data.BackupIndent; 7233 window->DC.GroupOffset = group_data.BackupGroupOffset; 7234 window->DC.CurrLineSize = group_data.BackupCurrLineSize; 7235 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; 7236 if (g.LogEnabled) 7237 g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return 7238 7239 if (!group_data.EmitItem) 7240 { 7241 window->DC.GroupStack.pop_back(); 7242 return; 7243 } 7244 7245 window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. 7246 ItemSize(group_bb.GetSize()); 7247 ItemAdd(group_bb, 0); 7248 7249 // 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. 7250 // 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. 7251 // Also if you grep for LastItemId you'll notice it is only used in that context. 7252 // (The tests not symmetrical because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) 7253 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; 7254 const bool group_contains_prev_active_id = !group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive; 7255 if (group_contains_curr_active_id) 7256 window->DC.LastItemId = g.ActiveId; 7257 else if (group_contains_prev_active_id) 7258 window->DC.LastItemId = g.ActiveIdPreviousFrame; 7259 window->DC.LastItemRect = group_bb; 7260 7261 // Forward Edited flag 7262 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) 7263 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; 7264 7265 // Forward Deactivated flag 7266 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; 7267 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) 7268 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; 7269 7270 window->DC.GroupStack.pop_back(); 7271 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] 7272} 7273 7274 7275//----------------------------------------------------------------------------- 7276// [SECTION] SCROLLING 7277//----------------------------------------------------------------------------- 7278 7279static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges) 7280{ 7281 ImGuiContext& g = *GImGui; 7282 ImVec2 scroll = window->Scroll; 7283 if (window->ScrollTarget.x < FLT_MAX) 7284 { 7285 float cr_x = window->ScrollTargetCenterRatio.x; 7286 float target_x = window->ScrollTarget.x; 7287 if (snap_on_edges && cr_x <= 0.0f && target_x <= window->WindowPadding.x) 7288 target_x = 0.0f; 7289 else if (snap_on_edges && cr_x >= 1.0f && target_x >= window->ContentSize.x + window->WindowPadding.x + g.Style.ItemSpacing.x) 7290 target_x = window->ContentSize.x + window->WindowPadding.x * 2.0f; 7291 scroll.x = target_x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x); 7292 } 7293 if (window->ScrollTarget.y < FLT_MAX) 7294 { 7295 // '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. 7296 float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 7297 float cr_y = window->ScrollTargetCenterRatio.y; 7298 float target_y = window->ScrollTarget.y; 7299 if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y) 7300 target_y = 0.0f; 7301 if (snap_on_edges && cr_y >= 1.0f && target_y >= window->ContentSize.y + window->WindowPadding.y + g.Style.ItemSpacing.y) 7302 target_y = window->ContentSize.y + window->WindowPadding.y * 2.0f; 7303 scroll.y = target_y - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y - decoration_up_height); 7304 } 7305 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); 7306 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); 7307 if (!window->Collapsed && !window->SkipItems) 7308 { 7309 scroll.x = ImMin(scroll.x, window->ScrollMax.x); 7310 scroll.y = ImMin(scroll.y, window->ScrollMax.y); 7311 } 7312 return scroll; 7313} 7314 7315// Scroll to keep newly navigated item fully into view 7316ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) 7317{ 7318 ImGuiContext& g = *GImGui; 7319 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); 7320 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] 7321 7322 ImVec2 delta_scroll; 7323 if (!window_rect.Contains(item_rect)) 7324 { 7325 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) 7326 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x + g.Style.ItemSpacing.x, 0.0f); 7327 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) 7328 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); 7329 if (item_rect.Min.y < window_rect.Min.y) 7330 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); 7331 else if (item_rect.Max.y >= window_rect.Max.y) 7332 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); 7333 7334 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window, false); 7335 delta_scroll = next_scroll - window->Scroll; 7336 } 7337 7338 // Also scroll parent window to keep us into view if necessary 7339 if (window->Flags & ImGuiWindowFlags_ChildWindow) 7340 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); 7341 7342 return delta_scroll; 7343} 7344 7345float ImGui::GetScrollX() 7346{ 7347 ImGuiWindow* window = GImGui->CurrentWindow; 7348 return window->Scroll.x; 7349} 7350 7351float ImGui::GetScrollY() 7352{ 7353 ImGuiWindow* window = GImGui->CurrentWindow; 7354 return window->Scroll.y; 7355} 7356 7357float ImGui::GetScrollMaxX() 7358{ 7359 ImGuiWindow* window = GImGui->CurrentWindow; 7360 return window->ScrollMax.x; 7361} 7362 7363float ImGui::GetScrollMaxY() 7364{ 7365 ImGuiWindow* window = GImGui->CurrentWindow; 7366 return window->ScrollMax.y; 7367} 7368 7369void ImGui::SetScrollX(float scroll_x) 7370{ 7371 ImGuiWindow* window = GetCurrentWindow(); 7372 window->ScrollTarget.x = scroll_x; 7373 window->ScrollTargetCenterRatio.x = 0.0f; 7374} 7375 7376void ImGui::SetScrollY(float scroll_y) 7377{ 7378 ImGuiWindow* window = GetCurrentWindow(); 7379 window->ScrollTarget.y = scroll_y; 7380 window->ScrollTargetCenterRatio.y = 0.0f; 7381} 7382 7383void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x) 7384{ 7385 window->ScrollTarget.x = new_scroll_x; 7386 window->ScrollTargetCenterRatio.x = 0.0f; 7387} 7388 7389void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y) 7390{ 7391 window->ScrollTarget.y = new_scroll_y; 7392 window->ScrollTargetCenterRatio.y = 0.0f; 7393} 7394 7395 7396void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) 7397{ 7398 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size 7399 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); 7400 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); 7401 window->ScrollTargetCenterRatio.x = center_x_ratio; 7402} 7403 7404void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) 7405{ 7406 // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size 7407 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); 7408 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 7409 local_y -= decoration_up_height; 7410 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); 7411 window->ScrollTargetCenterRatio.y = center_y_ratio; 7412} 7413 7414void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) 7415{ 7416 ImGuiContext& g = *GImGui; 7417 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio); 7418} 7419 7420void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) 7421{ 7422 ImGuiContext& g = *GImGui; 7423 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio); 7424} 7425 7426// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. 7427void ImGui::SetScrollHereX(float center_x_ratio) 7428{ 7429 ImGuiContext& g = *GImGui; 7430 ImGuiWindow* window = g.CurrentWindow; 7431 float target_x = window->DC.LastItemRect.Min.x - window->Pos.x; // Left of last item, in window space 7432 float last_item_width = window->DC.LastItemRect.GetWidth(); 7433 target_x += (last_item_width * center_x_ratio) + (g.Style.ItemSpacing.x * (center_x_ratio - 0.5f) * 2.0f); // Precisely aim before, in the middle or after the last item. 7434 SetScrollFromPosX(target_x, center_x_ratio); 7435} 7436 7437// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. 7438void ImGui::SetScrollHereY(float center_y_ratio) 7439{ 7440 ImGuiContext& g = *GImGui; 7441 ImGuiWindow* window = g.CurrentWindow; 7442 float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space 7443 target_y += (window->DC.PrevLineSize.y * center_y_ratio) + (g.Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. 7444 SetScrollFromPosY(target_y, center_y_ratio); 7445} 7446 7447//----------------------------------------------------------------------------- 7448// [SECTION] TOOLTIPS 7449//----------------------------------------------------------------------------- 7450 7451void ImGui::BeginTooltip() 7452{ 7453 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); 7454} 7455 7456void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) 7457{ 7458 ImGuiContext& g = *GImGui; 7459 7460 if (g.DragDropWithinSource || g.DragDropWithinTarget) 7461 { 7462 // 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) 7463 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. 7464 // 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. 7465 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; 7466 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); 7467 SetNextWindowPos(tooltip_pos); 7468 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); 7469 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( 7470 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; 7471 } 7472 7473 char window_name[16]; 7474 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); 7475 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) 7476 if (ImGuiWindow* window = FindWindowByName(window_name)) 7477 if (window->Active) 7478 { 7479 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. 7480 window->Hidden = true; 7481 window->HiddenFramesCanSkipItems = 1; 7482 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); 7483 } 7484 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; 7485 Begin(window_name, NULL, flags | extra_flags); 7486} 7487 7488void ImGui::EndTooltip() 7489{ 7490 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls 7491 End(); 7492} 7493 7494void ImGui::SetTooltipV(const char* fmt, va_list args) 7495{ 7496 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); 7497 TextV(fmt, args); 7498 EndTooltip(); 7499} 7500 7501void ImGui::SetTooltip(const char* fmt, ...) 7502{ 7503 va_list args; 7504 va_start(args, fmt); 7505 SetTooltipV(fmt, args); 7506 va_end(args); 7507} 7508 7509//----------------------------------------------------------------------------- 7510// [SECTION] POPUPS 7511//----------------------------------------------------------------------------- 7512 7513bool ImGui::IsPopupOpen(ImGuiID id) 7514{ 7515 ImGuiContext& g = *GImGui; 7516 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; 7517} 7518 7519bool ImGui::IsPopupOpen(const char* str_id) 7520{ 7521 ImGuiContext& g = *GImGui; 7522 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); 7523} 7524 7525ImGuiWindow* ImGui::GetTopMostPopupModal() 7526{ 7527 ImGuiContext& g = *GImGui; 7528 for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) 7529 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) 7530 if (popup->Flags & ImGuiWindowFlags_Modal) 7531 return popup; 7532 return NULL; 7533} 7534 7535void ImGui::OpenPopup(const char* str_id) 7536{ 7537 ImGuiContext& g = *GImGui; 7538 OpenPopupEx(g.CurrentWindow->GetID(str_id)); 7539} 7540 7541// Mark popup as open (toggle toward open state). 7542// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. 7543// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). 7544// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) 7545void ImGui::OpenPopupEx(ImGuiID id) 7546{ 7547 ImGuiContext& g = *GImGui; 7548 ImGuiWindow* parent_window = g.CurrentWindow; 7549 int current_stack_size = g.BeginPopupStack.Size; 7550 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. 7551 popup_ref.PopupId = id; 7552 popup_ref.Window = NULL; 7553 popup_ref.SourceWindow = g.NavWindow; 7554 popup_ref.OpenFrameCount = g.FrameCount; 7555 popup_ref.OpenParentId = parent_window->IDStack.back(); 7556 popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); 7557 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; 7558 7559 //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id); 7560 if (g.OpenPopupStack.Size < current_stack_size + 1) 7561 { 7562 g.OpenPopupStack.push_back(popup_ref); 7563 } 7564 else 7565 { 7566 // 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 7567 // 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 7568 // 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. 7569 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) 7570 { 7571 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; 7572 } 7573 else 7574 { 7575 // Close child popups if any, then flag popup for open/reopen 7576 g.OpenPopupStack.resize(current_stack_size + 1); 7577 g.OpenPopupStack[current_stack_size] = popup_ref; 7578 } 7579 7580 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). 7581 // This is equivalent to what ClosePopupToLevel() does. 7582 //if (g.OpenPopupStack[current_stack_size].PopupId == id) 7583 // FocusWindow(parent_window); 7584 } 7585} 7586 7587void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) 7588{ 7589 ImGuiContext& g = *GImGui; 7590 if (g.OpenPopupStack.empty()) 7591 return; 7592 7593 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. 7594 // Don't close our own child popup windows. 7595 int popup_count_to_keep = 0; 7596 if (ref_window) 7597 { 7598 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) 7599 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) 7600 { 7601 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; 7602 if (!popup.Window) 7603 continue; 7604 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); 7605 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) 7606 continue; 7607 7608 // Trim the stack when popups are not direct descendant of the reference window (the reference window is often the NavWindow) 7609 bool popup_or_descendent_is_ref_window = false; 7610 for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_is_ref_window; m++) 7611 if (ImGuiWindow* popup_window = g.OpenPopupStack[m].Window) 7612 if (popup_window->RootWindow == ref_window->RootWindow) 7613 popup_or_descendent_is_ref_window = true; 7614 if (!popup_or_descendent_is_ref_window) 7615 break; 7616 } 7617 } 7618 if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below 7619 { 7620 //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); 7621 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); 7622 } 7623} 7624 7625void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) 7626{ 7627 ImGuiContext& g = *GImGui; 7628 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); 7629 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; 7630 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; 7631 g.OpenPopupStack.resize(remaining); 7632 7633 if (restore_focus_to_window_under_popup) 7634 { 7635 if (focus_window && !focus_window->WasActive && popup_window) 7636 { 7637 // Fallback 7638 FocusTopMostWindowUnderOne(popup_window, NULL); 7639 } 7640 else 7641 { 7642 if (g.NavLayer == ImGuiNavLayer_Main && focus_window) 7643 focus_window = NavRestoreLastChildNavWindow(focus_window); 7644 FocusWindow(focus_window); 7645 } 7646 } 7647} 7648 7649// Close the popup we have begin-ed into. 7650void ImGui::CloseCurrentPopup() 7651{ 7652 ImGuiContext& g = *GImGui; 7653 int popup_idx = g.BeginPopupStack.Size - 1; 7654 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) 7655 return; 7656 7657 // Closing a menu closes its top-most parent popup (unless a modal) 7658 while (popup_idx > 0) 7659 { 7660 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window; 7661 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; 7662 bool close_parent = false; 7663 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) 7664 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) 7665 close_parent = true; 7666 if (!close_parent) 7667 break; 7668 popup_idx--; 7669 } 7670 //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); 7671 ClosePopupToLevel(popup_idx, true); 7672 7673 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. 7674 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. 7675 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic. 7676 if (ImGuiWindow* window = g.NavWindow) 7677 window->DC.NavHideHighlightOneFrame = true; 7678} 7679 7680bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) 7681{ 7682 ImGuiContext& g = *GImGui; 7683 if (!IsPopupOpen(id)) 7684 { 7685 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 7686 return false; 7687 } 7688 7689 char name[20]; 7690 if (flags & ImGuiWindowFlags_ChildMenu) 7691 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth 7692 else 7693 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame 7694 7695 flags |= ImGuiWindowFlags_Popup; 7696 bool is_open = Begin(name, NULL, flags); 7697 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) 7698 EndPopup(); 7699 7700 return is_open; 7701} 7702 7703bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) 7704{ 7705 ImGuiContext& g = *GImGui; 7706 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance 7707 { 7708 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 7709 return false; 7710 } 7711 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; 7712 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); 7713} 7714 7715// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. 7716// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. 7717bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) 7718{ 7719 ImGuiContext& g = *GImGui; 7720 ImGuiWindow* window = g.CurrentWindow; 7721 const ImGuiID id = window->GetID(name); 7722 if (!IsPopupOpen(id)) 7723 { 7724 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 7725 return false; 7726 } 7727 7728 // Center modal windows by default 7729 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. 7730 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) 7731 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); 7732 7733 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings; 7734 const bool is_open = Begin(name, p_open, flags); 7735 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) 7736 { 7737 EndPopup(); 7738 if (is_open) 7739 ClosePopupToLevel(g.BeginPopupStack.Size, true); 7740 return false; 7741 } 7742 return is_open; 7743} 7744 7745void ImGui::EndPopup() 7746{ 7747 ImGuiContext& g = *GImGui; 7748 ImGuiWindow* window = g.CurrentWindow; 7749 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls 7750 IM_ASSERT(g.BeginPopupStack.Size > 0); 7751 7752 // Make all menus and popups wrap around for now, may need to expose that policy. 7753 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); 7754 7755 // Child-popups don't need to be layed out 7756 IM_ASSERT(g.WithinEndChild == false); 7757 if (window->Flags & ImGuiWindowFlags_ChildWindow) 7758 g.WithinEndChild = true; 7759 End(); 7760 g.WithinEndChild = false; 7761} 7762 7763bool ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiMouseButton mouse_button) 7764{ 7765 ImGuiWindow* window = GImGui->CurrentWindow; 7766 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 7767 { 7768 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! 7769 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 7770 OpenPopupEx(id); 7771 return true; 7772 } 7773 return false; 7774} 7775 7776// This is a helper to handle the simplest case of associating one named popup to one given widget. 7777// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). 7778// You can pass a NULL str_id to use the identifier of the last item. 7779bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiMouseButton mouse_button) 7780{ 7781 ImGuiWindow* window = GImGui->CurrentWindow; 7782 if (window->SkipItems) 7783 return false; 7784 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! 7785 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 7786 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 7787 OpenPopupEx(id); 7788 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); 7789} 7790 7791bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mouse_button, bool also_over_items) 7792{ 7793 if (!str_id) 7794 str_id = "window_context"; 7795 ImGuiID id = GImGui->CurrentWindow->GetID(str_id); 7796 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 7797 if (also_over_items || !IsAnyItemHovered()) 7798 OpenPopupEx(id); 7799 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); 7800} 7801 7802bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiMouseButton mouse_button) 7803{ 7804 if (!str_id) 7805 str_id = "void_context"; 7806 ImGuiID id = GImGui->CurrentWindow->GetID(str_id); 7807 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) 7808 OpenPopupEx(id); 7809 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); 7810} 7811 7812// 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.) 7813// 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. 7814ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) 7815{ 7816 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); 7817 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); 7818 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); 7819 7820 // Combo Box policy (we want a connecting edge) 7821 if (policy == ImGuiPopupPositionPolicy_ComboBox) 7822 { 7823 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; 7824 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 7825 { 7826 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 7827 if (n != -1 && dir == *last_dir) // Already tried this direction? 7828 continue; 7829 ImVec2 pos; 7830 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) 7831 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right 7832 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left 7833 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left 7834 if (!r_outer.Contains(ImRect(pos, pos + size))) 7835 continue; 7836 *last_dir = dir; 7837 return pos; 7838 } 7839 } 7840 7841 // Default popup policy 7842 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; 7843 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 7844 { 7845 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 7846 if (n != -1 && dir == *last_dir) // Already tried this direction? 7847 continue; 7848 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); 7849 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); 7850 if (avail_w < size.x || avail_h < size.y) 7851 continue; 7852 ImVec2 pos; 7853 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; 7854 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; 7855 *last_dir = dir; 7856 return pos; 7857 } 7858 7859 // Fallback, try to keep within display 7860 *last_dir = ImGuiDir_None; 7861 ImVec2 pos = ref_pos; 7862 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); 7863 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); 7864 return pos; 7865} 7866 7867ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) 7868{ 7869 IM_UNUSED(window); 7870 ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; 7871 ImRect r_screen = GetViewportRect(); 7872 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); 7873 return r_screen; 7874} 7875 7876ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) 7877{ 7878 ImGuiContext& g = *GImGui; 7879 7880 ImRect r_outer = GetWindowAllowedExtentRect(window); 7881 if (window->Flags & ImGuiWindowFlags_ChildMenu) 7882 { 7883 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. 7884 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. 7885 IM_ASSERT(g.CurrentWindow == window); 7886 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; 7887 float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). 7888 ImRect r_avoid; 7889 if (parent_window->DC.MenuBarAppending) 7890 r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field 7891 else 7892 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); 7893 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); 7894 } 7895 if (window->Flags & ImGuiWindowFlags_Popup) 7896 { 7897 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); 7898 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); 7899 } 7900 if (window->Flags & ImGuiWindowFlags_Tooltip) 7901 { 7902 // Position tooltip (always follows mouse) 7903 float sc = g.Style.MouseCursorScale; 7904 ImVec2 ref_pos = NavCalcPreferredRefPos(); 7905 ImRect r_avoid; 7906 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) 7907 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); 7908 else 7909 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. 7910 ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); 7911 if (window->AutoPosLastDirection == ImGuiDir_None) 7912 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. 7913 return pos; 7914 } 7915 IM_ASSERT(0); 7916 return window->Pos; 7917} 7918 7919//----------------------------------------------------------------------------- 7920// [SECTION] KEYBOARD/GAMEPAD NAVIGATION 7921//----------------------------------------------------------------------------- 7922 7923// FIXME-NAV: The existence of SetNavID vs SetNavIDWithRectRel vs SetFocusID is incredibly messy and confusing, 7924// and needs some explanation or serious refactoring. 7925void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id) 7926{ 7927 ImGuiContext& g = *GImGui; 7928 IM_ASSERT(g.NavWindow); 7929 IM_ASSERT(nav_layer == 0 || nav_layer == 1); 7930 g.NavId = id; 7931 g.NavFocusScopeId = focus_scope_id; 7932 g.NavWindow->NavLastIds[nav_layer] = id; 7933} 7934 7935void ImGui::SetNavIDWithRectRel(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) 7936{ 7937 ImGuiContext& g = *GImGui; 7938 SetNavID(id, nav_layer, focus_scope_id); 7939 g.NavWindow->NavRectRel[nav_layer] = rect_rel; 7940 g.NavMousePosDirty = true; 7941 g.NavDisableHighlight = false; 7942 g.NavDisableMouseHover = true; 7943} 7944 7945void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) 7946{ 7947 ImGuiContext& g = *GImGui; 7948 IM_ASSERT(id != 0); 7949 7950 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid. 7951 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) 7952 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; 7953 if (g.NavWindow != window) 7954 g.NavInitRequest = false; 7955 g.NavWindow = window; 7956 g.NavId = id; 7957 g.NavLayer = nav_layer; 7958 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; 7959 window->NavLastIds[nav_layer] = id; 7960 if (window->DC.LastItemId == id) 7961 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); 7962 7963 if (g.ActiveIdSource == ImGuiInputSource_Nav) 7964 g.NavDisableMouseHover = true; 7965 else 7966 g.NavDisableHighlight = true; 7967} 7968 7969ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) 7970{ 7971 if (ImFabs(dx) > ImFabs(dy)) 7972 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; 7973 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; 7974} 7975 7976static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) 7977{ 7978 if (a1 < b0) 7979 return a1 - b0; 7980 if (b1 < a0) 7981 return a0 - b1; 7982 return 0.0f; 7983} 7984 7985static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) 7986{ 7987 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) 7988 { 7989 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); 7990 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); 7991 } 7992 else 7993 { 7994 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); 7995 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); 7996 } 7997} 7998 7999// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 8000static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) 8001{ 8002 ImGuiContext& g = *GImGui; 8003 ImGuiWindow* window = g.CurrentWindow; 8004 if (g.NavLayer != window->DC.NavLayerCurrent) 8005 return false; 8006 8007 const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) 8008 g.NavScoringCount++; 8009 8010 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring 8011 if (window->ParentWindow == g.NavWindow) 8012 { 8013 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); 8014 if (!window->ClipRect.Overlaps(cand)) 8015 return false; 8016 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window 8017 } 8018 8019 // 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) 8020 // For example, this ensure that items in one column are not reached when moving vertically from items in another column. 8021 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); 8022 8023 // Compute distance between boxes 8024 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. 8025 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); 8026 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 8027 if (dby != 0.0f && dbx != 0.0f) 8028 dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); 8029 float dist_box = ImFabs(dbx) + ImFabs(dby); 8030 8031 // 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) 8032 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); 8033 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); 8034 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) 8035 8036 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance 8037 ImGuiDir quadrant; 8038 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; 8039 if (dbx != 0.0f || dby != 0.0f) 8040 { 8041 // For non-overlapping boxes, use distance between boxes 8042 dax = dbx; 8043 day = dby; 8044 dist_axial = dist_box; 8045 quadrant = ImGetDirQuadrantFromDelta(dbx, dby); 8046 } 8047 else if (dcx != 0.0f || dcy != 0.0f) 8048 { 8049 // For overlapping boxes with different centers, use distance between centers 8050 dax = dcx; 8051 day = dcy; 8052 dist_axial = dist_center; 8053 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); 8054 } 8055 else 8056 { 8057 // 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) 8058 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; 8059 } 8060 8061#if IMGUI_DEBUG_NAV_SCORING 8062 char buf[128]; 8063 if (IsMouseHoveringRect(cand.Min, cand.Max)) 8064 { 8065 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]); 8066 ImDrawList* draw_list = GetForegroundDrawList(window); 8067 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); 8068 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); 8069 draw_list->AddRectFilled(cand.Max - ImVec2(4,4), cand.Max + CalcTextSize(buf) + ImVec2(4,4), IM_COL32(40,0,0,150)); 8070 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); 8071 } 8072 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. 8073 { 8074 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } 8075 if (quadrant == g.NavMoveDir) 8076 { 8077 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); 8078 ImDrawList* draw_list = GetForegroundDrawList(window); 8079 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); 8080 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); 8081 } 8082 } 8083 #endif 8084 8085 // Is it in the quadrant we're interesting in moving to? 8086 bool new_best = false; 8087 if (quadrant == g.NavMoveDir) 8088 { 8089 // Does it beat the current best candidate? 8090 if (dist_box < result->DistBox) 8091 { 8092 result->DistBox = dist_box; 8093 result->DistCenter = dist_center; 8094 return true; 8095 } 8096 if (dist_box == result->DistBox) 8097 { 8098 // Try using distance between center points to break ties 8099 if (dist_center < result->DistCenter) 8100 { 8101 result->DistCenter = dist_center; 8102 new_best = true; 8103 } 8104 else if (dist_center == result->DistCenter) 8105 { 8106 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items 8107 // (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), 8108 // 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. 8109 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance 8110 new_best = true; 8111 } 8112 } 8113 } 8114 8115 // 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 8116 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) 8117 // 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. 8118 // 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. 8119 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? 8120 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match 8121 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) 8122 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)) 8123 { 8124 result->DistAxial = dist_axial; 8125 new_best = true; 8126 } 8127 8128 return new_best; 8129} 8130 8131// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) 8132static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) 8133{ 8134 ImGuiContext& g = *GImGui; 8135 //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. 8136 // return; 8137 8138 const ImGuiItemFlags item_flags = window->DC.ItemFlags; 8139 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); 8140 8141 // Process Init Request 8142 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) 8143 { 8144 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback 8145 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) 8146 { 8147 g.NavInitResultId = id; 8148 g.NavInitResultRectRel = nav_bb_rel; 8149 } 8150 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) 8151 { 8152 g.NavInitRequest = false; // Found a match, clear request 8153 NavUpdateAnyRequestFlag(); 8154 } 8155 } 8156 8157 // Process Move Request (scoring for navigation) 8158 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) 8159 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav))) 8160 { 8161 ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 8162#if IMGUI_DEBUG_NAV_SCORING 8163 // [DEBUG] Score all items in NavWindow at all times 8164 if (!g.NavMoveRequest) 8165 g.NavMoveDir = g.NavMoveDirLast; 8166 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; 8167#else 8168 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); 8169#endif 8170 if (new_best) 8171 { 8172 result->Window = window; 8173 result->ID = id; 8174 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; 8175 result->RectRel = nav_bb_rel; 8176 } 8177 8178 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. 8179 const float VISIBLE_RATIO = 0.70f; 8180 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) 8181 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) 8182 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) 8183 { 8184 result = &g.NavMoveResultLocalVisibleSet; 8185 result->Window = window; 8186 result->ID = id; 8187 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; 8188 result->RectRel = nav_bb_rel; 8189 } 8190 } 8191 8192 // Update window-relative bounding box of navigated item 8193 if (g.NavId == id) 8194 { 8195 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. 8196 g.NavLayer = window->DC.NavLayerCurrent; 8197 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; 8198 g.NavIdIsAlive = true; 8199 g.NavIdTabCounter = window->DC.FocusCounterTabStop; 8200 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) 8201 } 8202} 8203 8204bool ImGui::NavMoveRequestButNoResultYet() 8205{ 8206 ImGuiContext& g = *GImGui; 8207 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; 8208} 8209 8210void ImGui::NavMoveRequestCancel() 8211{ 8212 ImGuiContext& g = *GImGui; 8213 g.NavMoveRequest = false; 8214 NavUpdateAnyRequestFlag(); 8215} 8216 8217void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) 8218{ 8219 ImGuiContext& g = *GImGui; 8220 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); 8221 NavMoveRequestCancel(); 8222 g.NavMoveDir = move_dir; 8223 g.NavMoveClipDir = clip_dir; 8224 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; 8225 g.NavMoveRequestFlags = move_flags; 8226 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; 8227} 8228 8229void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) 8230{ 8231 ImGuiContext& g = *GImGui; 8232 if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != ImGuiNavLayer_Main) 8233 return; 8234 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping 8235 ImRect bb_rel = window->NavRectRel[0]; 8236 8237 ImGuiDir clip_dir = g.NavMoveDir; 8238 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) 8239 { 8240 bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; 8241 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; } 8242 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 8243 } 8244 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) 8245 { 8246 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; 8247 if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; } 8248 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 8249 } 8250 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) 8251 { 8252 bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; 8253 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; } 8254 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 8255 } 8256 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) 8257 { 8258 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; 8259 if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; } 8260 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 8261 } 8262} 8263 8264// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). 8265// This way we could find the last focused window among our children. It would be much less confusing this way? 8266static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) 8267{ 8268 ImGuiWindow* parent_window = nav_window; 8269 while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 8270 parent_window = parent_window->ParentWindow; 8271 if (parent_window && parent_window != nav_window) 8272 parent_window->NavLastChildNavWindow = nav_window; 8273} 8274 8275// Restore the last focused child. 8276// Call when we are expected to land on the Main Layer (0) after FocusWindow() 8277static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) 8278{ 8279 return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; 8280} 8281 8282static void NavRestoreLayer(ImGuiNavLayer layer) 8283{ 8284 ImGuiContext& g = *GImGui; 8285 g.NavLayer = layer; 8286 if (layer == 0) 8287 g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); 8288 ImGuiWindow* window = g.NavWindow; 8289 if (layer == 0 && window->NavLastIds[0] != 0) 8290 ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]); 8291 else 8292 ImGui::NavInitWindow(window, true); 8293} 8294 8295static inline void ImGui::NavUpdateAnyRequestFlag() 8296{ 8297 ImGuiContext& g = *GImGui; 8298 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); 8299 if (g.NavAnyRequest) 8300 IM_ASSERT(g.NavWindow != NULL); 8301} 8302 8303// This needs to be called before we submit any widget (aka in or before Begin) 8304void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) 8305{ 8306 ImGuiContext& g = *GImGui; 8307 IM_ASSERT(window == g.NavWindow); 8308 bool init_for_nav = false; 8309 if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) 8310 if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) 8311 init_for_nav = true; 8312 //IMGUI_DEBUG_LOG("[Nav] NavInitWindow() init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); 8313 if (init_for_nav) 8314 { 8315 SetNavID(0, g.NavLayer, 0); 8316 g.NavInitRequest = true; 8317 g.NavInitRequestFromMove = false; 8318 g.NavInitResultId = 0; 8319 g.NavInitResultRectRel = ImRect(); 8320 NavUpdateAnyRequestFlag(); 8321 } 8322 else 8323 { 8324 g.NavId = window->NavLastIds[0]; 8325 g.NavFocusScopeId = 0; 8326 } 8327} 8328 8329static ImVec2 ImGui::NavCalcPreferredRefPos() 8330{ 8331 ImGuiContext& g = *GImGui; 8332 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) 8333 { 8334 // Mouse (we need a fallback in case the mouse becomes invalid after being used) 8335 if (IsMousePosValid(&g.IO.MousePos)) 8336 return g.IO.MousePos; 8337 return g.LastValidMousePos; 8338 } 8339 else 8340 { 8341 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. 8342 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; 8343 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())); 8344 ImRect visible_rect = GetViewportRect(); 8345 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. 8346 } 8347} 8348 8349float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) 8350{ 8351 ImGuiContext& g = *GImGui; 8352 if (mode == ImGuiInputReadMode_Down) 8353 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) 8354 8355 const float t = g.IO.NavInputsDownDuration[n]; 8356 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. 8357 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); 8358 if (t < 0.0f) 8359 return 0.0f; 8360 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. 8361 return (t == 0.0f) ? 1.0f : 0.0f; 8362 if (mode == ImGuiInputReadMode_Repeat) 8363 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f); 8364 if (mode == ImGuiInputReadMode_RepeatSlow) 8365 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f); 8366 if (mode == ImGuiInputReadMode_RepeatFast) 8367 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f); 8368 return 0.0f; 8369} 8370 8371ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) 8372{ 8373 ImVec2 delta(0.0f, 0.0f); 8374 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) 8375 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); 8376 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) 8377 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); 8378 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) 8379 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); 8380 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) 8381 delta *= slow_factor; 8382 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) 8383 delta *= fast_factor; 8384 return delta; 8385} 8386 8387static void ImGui::NavUpdate() 8388{ 8389 ImGuiContext& g = *GImGui; 8390 g.IO.WantSetMousePos = false; 8391#if 0 8392 if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); 8393#endif 8394 8395 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) 8396 // (do it before we map Keyboard input!) 8397 bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; 8398 bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; 8399 if (nav_gamepad_active) 8400 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) 8401 g.NavInputSource = ImGuiInputSource_NavGamepad; 8402 8403 // Update Keyboard->Nav inputs mapping 8404 if (nav_keyboard_active) 8405 { 8406 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } } while (0) 8407 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); 8408 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); 8409 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); 8410 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); 8411 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); 8412 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); 8413 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); 8414 if (g.IO.KeyCtrl) 8415 g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; 8416 if (g.IO.KeyShift) 8417 g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; 8418 if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. 8419 g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; 8420 #undef NAV_MAP_KEY 8421 } 8422 memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); 8423 for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) 8424 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; 8425 8426 // Process navigation init request (select first/default focus) 8427 // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) 8428 if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove) && g.NavWindow) 8429 { 8430 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) 8431 //IMGUI_DEBUG_LOG("[Nav] Apply NavInitRequest result: 0x%08X Layer %d in \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); 8432 if (g.NavInitRequestFromMove) 8433 SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); 8434 else 8435 SetNavID(g.NavInitResultId, g.NavLayer, 0); 8436 g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; 8437 } 8438 g.NavInitRequest = false; 8439 g.NavInitRequestFromMove = false; 8440 g.NavInitResultId = 0; 8441 g.NavJustMovedToId = 0; 8442 8443 // Process navigation move request 8444 if (g.NavMoveRequest) 8445 NavUpdateMoveResult(); 8446 8447 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame 8448 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) 8449 { 8450 IM_ASSERT(g.NavMoveRequest); 8451 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 8452 g.NavDisableHighlight = false; 8453 g.NavMoveRequestForward = ImGuiNavForward_None; 8454 } 8455 8456 // Apply application mouse position movement, after we had a chance to process move request result. 8457 if (g.NavMousePosDirty && g.NavIdIsAlive) 8458 { 8459 // Set mouse position given our knowledge of the navigated item position from last frame 8460 if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) 8461 { 8462 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) 8463 { 8464 g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); 8465 g.IO.WantSetMousePos = true; 8466 } 8467 } 8468 g.NavMousePosDirty = false; 8469 } 8470 g.NavIdIsAlive = false; 8471 g.NavJustTabbedId = 0; 8472 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); 8473 8474 // 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 8475 if (g.NavWindow) 8476 NavSaveLastChildNavWindowIntoParent(g.NavWindow); 8477 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) 8478 g.NavWindow->NavLastChildNavWindow = NULL; 8479 8480 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) 8481 NavUpdateWindowing(); 8482 8483 // Set output flags for user application 8484 g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); 8485 g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); 8486 8487 // Process NavCancel input (to close a popup, get back to parent, clear focus) 8488 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) 8489 { 8490 if (g.ActiveId != 0) 8491 { 8492 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) 8493 ClearActiveID(); 8494 } 8495 else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) 8496 { 8497 // Exit child window 8498 ImGuiWindow* child_window = g.NavWindow; 8499 ImGuiWindow* parent_window = g.NavWindow->ParentWindow; 8500 IM_ASSERT(child_window->ChildId != 0); 8501 FocusWindow(parent_window); 8502 SetNavID(child_window->ChildId, 0, 0); 8503 // Reassigning with same value, we're being explicit here. 8504 g.NavIdIsAlive = false; // -V1048 8505 if (g.NavDisableMouseHover) 8506 g.NavMousePosDirty = true; 8507 } 8508 else if (g.OpenPopupStack.Size > 0) 8509 { 8510 // Close open popup/menu 8511 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) 8512 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); 8513 } 8514 else if (g.NavLayer != ImGuiNavLayer_Main) 8515 { 8516 // Leave the "menu" layer 8517 NavRestoreLayer(ImGuiNavLayer_Main); 8518 } 8519 else 8520 { 8521 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were 8522 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) 8523 g.NavWindow->NavLastIds[0] = 0; 8524 g.NavId = g.NavFocusScopeId = 0; 8525 } 8526 } 8527 8528 // Process manual activation request 8529 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; 8530 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 8531 { 8532 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); 8533 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); 8534 if (g.ActiveId == 0 && activate_pressed) 8535 g.NavActivateId = g.NavId; 8536 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) 8537 g.NavActivateDownId = g.NavId; 8538 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) 8539 g.NavActivatePressedId = g.NavId; 8540 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) 8541 g.NavInputId = g.NavId; 8542 } 8543 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 8544 g.NavDisableHighlight = true; 8545 if (g.NavActivateId != 0) 8546 IM_ASSERT(g.NavActivateDownId == g.NavActivateId); 8547 g.NavMoveRequest = false; 8548 8549 // Process programmatic activation request 8550 if (g.NavNextActivateId != 0) 8551 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; 8552 g.NavNextActivateId = 0; 8553 8554 // Initiate directional inputs request 8555 if (g.NavMoveRequestForward == ImGuiNavForward_None) 8556 { 8557 g.NavMoveDir = ImGuiDir_None; 8558 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; 8559 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 8560 { 8561 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; 8562 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } 8563 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } 8564 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } 8565 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } 8566 } 8567 g.NavMoveClipDir = g.NavMoveDir; 8568 } 8569 else 8570 { 8571 // 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) 8572 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) 8573 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); 8574 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); 8575 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; 8576 } 8577 8578 // Update PageUp/PageDown/Home/End scroll 8579 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? 8580 float nav_scoring_rect_offset_y = 0.0f; 8581 if (nav_keyboard_active) 8582 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); 8583 8584 // 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 8585 if (g.NavMoveDir != ImGuiDir_None) 8586 { 8587 g.NavMoveRequest = true; 8588 g.NavMoveRequestKeyMods = g.IO.KeyMods; 8589 g.NavMoveDirLast = g.NavMoveDir; 8590 } 8591 if (g.NavMoveRequest && g.NavId == 0) 8592 { 8593 //IMGUI_DEBUG_LOG("[Nav] NavInitRequest from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); 8594 g.NavInitRequest = g.NavInitRequestFromMove = true; 8595 // Reassigning with same value, we're being explicit here. 8596 g.NavInitResultId = 0; // -V1048 8597 g.NavDisableHighlight = false; 8598 } 8599 NavUpdateAnyRequestFlag(); 8600 8601 // Scrolling 8602 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) 8603 { 8604 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item 8605 ImGuiWindow* window = g.NavWindow; 8606 const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * g.IO.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. 8607 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) 8608 { 8609 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) 8610 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); 8611 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) 8612 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); 8613 } 8614 8615 // *Normal* Manual scroll with NavScrollXXX keys 8616 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. 8617 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); 8618 if (scroll_dir.x != 0.0f && window->ScrollbarX) 8619 { 8620 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); 8621 g.NavMoveFromClampedRefRect = true; 8622 } 8623 if (scroll_dir.y != 0.0f) 8624 { 8625 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); 8626 g.NavMoveFromClampedRefRect = true; 8627 } 8628 } 8629 8630 // Reset search results 8631 g.NavMoveResultLocal.Clear(); 8632 g.NavMoveResultLocalVisibleSet.Clear(); 8633 g.NavMoveResultOther.Clear(); 8634 8635 // 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 8636 if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == ImGuiNavLayer_Main) 8637 { 8638 ImGuiWindow* window = g.NavWindow; 8639 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1,1), window->InnerRect.Max - window->Pos + ImVec2(1,1)); 8640 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) 8641 { 8642 float pad = window->CalcFontSize() * 0.5f; 8643 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 8644 window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); 8645 g.NavId = g.NavFocusScopeId = 0; 8646 } 8647 g.NavMoveFromClampedRefRect = false; 8648 } 8649 8650 // 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) 8651 ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); 8652 g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); 8653 g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); 8654 g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); 8655 g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; 8656 IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). 8657 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] 8658 g.NavScoringCount = 0; 8659#if IMGUI_DEBUG_NAV_RECTS 8660 if (g.NavWindow) 8661 { 8662 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); 8663 if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] 8664 if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } 8665 } 8666#endif 8667} 8668 8669// Apply result from previous frame navigation directional move request 8670static void ImGui::NavUpdateMoveResult() 8671{ 8672 ImGuiContext& g = *GImGui; 8673 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 8674 { 8675 // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) 8676 if (g.NavId != 0) 8677 { 8678 g.NavDisableHighlight = false; 8679 g.NavDisableMouseHover = true; 8680 } 8681 return; 8682 } 8683 8684 // Select which result to use 8685 ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 8686 8687 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. 8688 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) 8689 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) 8690 result = &g.NavMoveResultLocalVisibleSet; 8691 8692 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. 8693 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) 8694 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) 8695 result = &g.NavMoveResultOther; 8696 IM_ASSERT(g.NavWindow && result->Window); 8697 8698 // Scroll to keep newly navigated item fully into view. 8699 if (g.NavLayer == ImGuiNavLayer_Main) 8700 { 8701 ImVec2 delta_scroll; 8702 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) 8703 { 8704 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; 8705 delta_scroll.y = result->Window->Scroll.y - scroll_target; 8706 SetScrollY(result->Window, scroll_target); 8707 } 8708 else 8709 { 8710 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); 8711 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); 8712 } 8713 8714 // Offset our result position so mouse position can be applied immediately after in NavUpdate() 8715 result->RectRel.TranslateX(-delta_scroll.x); 8716 result->RectRel.TranslateY(-delta_scroll.y); 8717 } 8718 8719 ClearActiveID(); 8720 g.NavWindow = result->Window; 8721 if (g.NavId != result->ID) 8722 { 8723 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) 8724 g.NavJustMovedToId = result->ID; 8725 g.NavJustMovedToFocusScopeId = result->FocusScopeId; 8726 g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; 8727 } 8728 SetNavIDWithRectRel(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); 8729 g.NavMoveFromClampedRefRect = false; 8730} 8731 8732// Handle PageUp/PageDown/Home/End keys 8733static float ImGui::NavUpdatePageUpPageDown() 8734{ 8735 ImGuiContext& g = *GImGui; 8736 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) 8737 return 0.0f; 8738 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) 8739 return 0.0f; 8740 8741 ImGuiWindow* window = g.NavWindow; 8742 const bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); 8743 const bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); 8744 const bool home_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); 8745 const bool end_pressed = IsKeyPressed(g.IO.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); 8746 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed 8747 { 8748 if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) 8749 { 8750 // Fallback manual-scroll when window has no navigable item 8751 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) 8752 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); 8753 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) 8754 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); 8755 else if (home_pressed) 8756 SetScrollY(window, 0.0f); 8757 else if (end_pressed) 8758 SetScrollY(window, window->ScrollMax.y); 8759 } 8760 else 8761 { 8762 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; 8763 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); 8764 float nav_scoring_rect_offset_y = 0.0f; 8765 if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) 8766 { 8767 nav_scoring_rect_offset_y = -page_offset_y; 8768 g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) 8769 g.NavMoveClipDir = ImGuiDir_Up; 8770 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; 8771 } 8772 else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) 8773 { 8774 nav_scoring_rect_offset_y = +page_offset_y; 8775 g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) 8776 g.NavMoveClipDir = ImGuiDir_Down; 8777 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; 8778 } 8779 else if (home_pressed) 8780 { 8781 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y 8782 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. 8783 // Preserve current horizontal position if we have any. 8784 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; 8785 if (nav_rect_rel.IsInverted()) 8786 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; 8787 g.NavMoveDir = ImGuiDir_Down; 8788 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; 8789 } 8790 else if (end_pressed) 8791 { 8792 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; 8793 if (nav_rect_rel.IsInverted()) 8794 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; 8795 g.NavMoveDir = ImGuiDir_Up; 8796 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; 8797 } 8798 return nav_scoring_rect_offset_y; 8799 } 8800 } 8801 return 0.0f; 8802} 8803 8804static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) 8805{ 8806 ImGuiContext& g = *GImGui; 8807 for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--) 8808 if (g.WindowsFocusOrder[i] == window) 8809 return i; 8810 return -1; 8811} 8812 8813static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) 8814{ 8815 ImGuiContext& g = *GImGui; 8816 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) 8817 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) 8818 return g.WindowsFocusOrder[i]; 8819 return NULL; 8820} 8821 8822static void NavUpdateWindowingHighlightWindow(int focus_change_dir) 8823{ 8824 ImGuiContext& g = *GImGui; 8825 IM_ASSERT(g.NavWindowingTarget); 8826 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) 8827 return; 8828 8829 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget); 8830 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); 8831 if (!window_target) 8832 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); 8833 if (window_target) // Don't reset windowing target if there's a single window in the list 8834 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; 8835 g.NavWindowingToggleLayer = false; 8836} 8837 8838// Windowing management mode 8839// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) 8840// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) 8841static void ImGui::NavUpdateWindowing() 8842{ 8843 ImGuiContext& g = *GImGui; 8844 ImGuiWindow* apply_focus_window = NULL; 8845 bool apply_toggle_layer = false; 8846 8847 ImGuiWindow* modal_window = GetTopMostPopupModal(); 8848 if (modal_window != NULL) 8849 { 8850 g.NavWindowingTarget = NULL; 8851 return; 8852 } 8853 8854 // Fade out 8855 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) 8856 { 8857 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); 8858 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) 8859 g.NavWindowingTargetAnim = NULL; 8860 } 8861 8862 // Start CTRL-TAB or Square+L/R window selection 8863 bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); 8864 bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); 8865 if (start_windowing_with_gamepad || start_windowing_with_keyboard) 8866 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) 8867 { 8868 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop 8869 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; 8870 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; 8871 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; 8872 } 8873 8874 // Gamepad update 8875 g.NavWindowingTimer += g.IO.DeltaTime; 8876 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) 8877 { 8878 // 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 8879 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); 8880 8881 // Select window to focus 8882 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); 8883 if (focus_change_dir != 0) 8884 { 8885 NavUpdateWindowingHighlightWindow(focus_change_dir); 8886 g.NavWindowingHighlightAlpha = 1.0f; 8887 } 8888 8889 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) 8890 if (!IsNavInputDown(ImGuiNavInput_Menu)) 8891 { 8892 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. 8893 if (g.NavWindowingToggleLayer && g.NavWindow) 8894 apply_toggle_layer = true; 8895 else if (!g.NavWindowingToggleLayer) 8896 apply_focus_window = g.NavWindowingTarget; 8897 g.NavWindowingTarget = NULL; 8898 } 8899 } 8900 8901 // Keyboard: Focus 8902 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) 8903 { 8904 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise 8905 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f 8906 if (IsKeyPressedMap(ImGuiKey_Tab, true)) 8907 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); 8908 if (!g.IO.KeyCtrl) 8909 apply_focus_window = g.NavWindowingTarget; 8910 } 8911 8912 // Keyboard: Press and Release ALT to toggle menu layer 8913 // 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 8914 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) 8915 g.NavWindowingToggleLayer = true; 8916 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) 8917 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) 8918 apply_toggle_layer = true; 8919 8920 // Move window 8921 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) 8922 { 8923 ImVec2 move_delta; 8924 if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) 8925 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 8926 if (g.NavInputSource == ImGuiInputSource_NavGamepad) 8927 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); 8928 if (move_delta.x != 0.0f || move_delta.y != 0.0f) 8929 { 8930 const float NAV_MOVE_SPEED = 800.0f; 8931 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 8932 SetWindowPos(g.NavWindowingTarget->RootWindow, g.NavWindowingTarget->RootWindow->Pos + move_delta * move_speed, ImGuiCond_Always); 8933 g.NavDisableMouseHover = true; 8934 MarkIniSettingsDirty(g.NavWindowingTarget); 8935 } 8936 } 8937 8938 // Apply final focus 8939 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) 8940 { 8941 ClearActiveID(); 8942 g.NavDisableHighlight = false; 8943 g.NavDisableMouseHover = true; 8944 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); 8945 ClosePopupsOverWindow(apply_focus_window, false); 8946 FocusWindow(apply_focus_window); 8947 if (apply_focus_window->NavLastIds[0] == 0) 8948 NavInitWindow(apply_focus_window, false); 8949 8950 // If the window only has a menu layer, select it directly 8951 if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) 8952 g.NavLayer = ImGuiNavLayer_Menu; 8953 } 8954 if (apply_focus_window) 8955 g.NavWindowingTarget = NULL; 8956 8957 // Apply menu/layer toggle 8958 if (apply_toggle_layer && g.NavWindow) 8959 { 8960 // Move to parent menu if necessary 8961 ImGuiWindow* new_nav_window = g.NavWindow; 8962 while (new_nav_window->ParentWindow 8963 && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 8964 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 8965 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 8966 new_nav_window = new_nav_window->ParentWindow; 8967 if (new_nav_window != g.NavWindow) 8968 { 8969 ImGuiWindow* old_nav_window = g.NavWindow; 8970 FocusWindow(new_nav_window); 8971 new_nav_window->NavLastChildNavWindow = old_nav_window; 8972 } 8973 g.NavDisableHighlight = false; 8974 g.NavDisableMouseHover = true; 8975 8976 // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. 8977 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; 8978 NavRestoreLayer(new_nav_layer); 8979 } 8980} 8981 8982// Window has already passed the IsWindowNavFocusable() 8983static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) 8984{ 8985 if (window->Flags & ImGuiWindowFlags_Popup) 8986 return "(Popup)"; 8987 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) 8988 return "(Main menu bar)"; 8989 return "(Untitled)"; 8990} 8991 8992// Overlay displayed when using CTRL+TAB. Called by EndFrame(). 8993void ImGui::NavUpdateWindowingOverlay() 8994{ 8995 ImGuiContext& g = *GImGui; 8996 IM_ASSERT(g.NavWindowingTarget != NULL); 8997 8998 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) 8999 return; 9000 9001 if (g.NavWindowingList == NULL) 9002 g.NavWindowingList = FindWindowByName("###NavWindowingList"); 9003 SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); 9004 SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 9005 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); 9006 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); 9007 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) 9008 { 9009 ImGuiWindow* window = g.WindowsFocusOrder[n]; 9010 if (!IsWindowNavFocusable(window)) 9011 continue; 9012 const char* label = window->Name; 9013 if (label == FindRenderedTextEnd(label)) 9014 label = GetFallbackWindowNameForWindowingList(window); 9015 Selectable(label, g.NavWindowingTarget == window); 9016 } 9017 End(); 9018 PopStyleVar(); 9019} 9020 9021//----------------------------------------------------------------------------- 9022// [SECTION] DRAG AND DROP 9023//----------------------------------------------------------------------------- 9024 9025void ImGui::ClearDragDrop() 9026{ 9027 ImGuiContext& g = *GImGui; 9028 g.DragDropActive = false; 9029 g.DragDropPayload.Clear(); 9030 g.DragDropAcceptFlags = ImGuiDragDropFlags_None; 9031 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; 9032 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 9033 g.DragDropAcceptFrameCount = -1; 9034 9035 g.DragDropPayloadBufHeap.clear(); 9036 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 9037} 9038 9039// Call when current ID is active. 9040// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() 9041bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) 9042{ 9043 ImGuiContext& g = *GImGui; 9044 ImGuiWindow* window = g.CurrentWindow; 9045 9046 bool source_drag_active = false; 9047 ImGuiID source_id = 0; 9048 ImGuiID source_parent_id = 0; 9049 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; 9050 if (!(flags & ImGuiDragDropFlags_SourceExtern)) 9051 { 9052 source_id = window->DC.LastItemId; 9053 if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case 9054 return false; 9055 if (g.IO.MouseDown[mouse_button] == false) 9056 return false; 9057 9058 if (source_id == 0) 9059 { 9060 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: 9061 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. 9062 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) 9063 { 9064 IM_ASSERT(0); 9065 return false; 9066 } 9067 9068 // Early out 9069 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) 9070 return false; 9071 9072 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() 9073 // We build a throwaway ID based on current ID stack + relative AABB of items in window. 9074 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. 9075 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. 9076 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); 9077 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); 9078 if (is_hovered && g.IO.MouseClicked[mouse_button]) 9079 { 9080 SetActiveID(source_id, window); 9081 FocusWindow(window); 9082 } 9083 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. 9084 g.ActiveIdAllowOverlap = is_hovered; 9085 } 9086 else 9087 { 9088 g.ActiveIdAllowOverlap = false; 9089 } 9090 if (g.ActiveId != source_id) 9091 return false; 9092 source_parent_id = window->IDStack.back(); 9093 source_drag_active = IsMouseDragging(mouse_button); 9094 9095 // Disable navigation and key inputs while dragging 9096 g.ActiveIdUsingNavDirMask = ~(ImU32)0; 9097 g.ActiveIdUsingNavInputMask = ~(ImU32)0; 9098 g.ActiveIdUsingKeyInputMask = ~(ImU64)0; 9099 } 9100 else 9101 { 9102 window = NULL; 9103 source_id = ImHashStr("#SourceExtern"); 9104 source_drag_active = true; 9105 } 9106 9107 if (source_drag_active) 9108 { 9109 if (!g.DragDropActive) 9110 { 9111 IM_ASSERT(source_id != 0); 9112 ClearDragDrop(); 9113 ImGuiPayload& payload = g.DragDropPayload; 9114 payload.SourceId = source_id; 9115 payload.SourceParentId = source_parent_id; 9116 g.DragDropActive = true; 9117 g.DragDropSourceFlags = flags; 9118 g.DragDropMouseButton = mouse_button; 9119 } 9120 g.DragDropSourceFrameCount = g.FrameCount; 9121 g.DragDropWithinSource = true; 9122 9123 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 9124 { 9125 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) 9126 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. 9127 BeginTooltip(); 9128 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) 9129 { 9130 ImGuiWindow* tooltip_window = g.CurrentWindow; 9131 tooltip_window->SkipItems = true; 9132 tooltip_window->HiddenFramesCanSkipItems = 1; 9133 } 9134 } 9135 9136 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) 9137 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; 9138 9139 return true; 9140 } 9141 return false; 9142} 9143 9144void ImGui::EndDragDropSource() 9145{ 9146 ImGuiContext& g = *GImGui; 9147 IM_ASSERT(g.DragDropActive); 9148 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?"); 9149 9150 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 9151 EndTooltip(); 9152 9153 // Discard the drag if have not called SetDragDropPayload() 9154 if (g.DragDropPayload.DataFrameCount == -1) 9155 ClearDragDrop(); 9156 g.DragDropWithinSource = false; 9157} 9158 9159// Use 'cond' to choose to submit payload on drag start or every frame 9160bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) 9161{ 9162 ImGuiContext& g = *GImGui; 9163 ImGuiPayload& payload = g.DragDropPayload; 9164 if (cond == 0) 9165 cond = ImGuiCond_Always; 9166 9167 IM_ASSERT(type != NULL); 9168 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); 9169 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); 9170 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); 9171 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() 9172 9173 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) 9174 { 9175 // Copy payload 9176 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); 9177 g.DragDropPayloadBufHeap.resize(0); 9178 if (data_size > sizeof(g.DragDropPayloadBufLocal)) 9179 { 9180 // Store in heap 9181 g.DragDropPayloadBufHeap.resize((int)data_size); 9182 payload.Data = g.DragDropPayloadBufHeap.Data; 9183 memcpy(payload.Data, data, data_size); 9184 } 9185 else if (data_size > 0) 9186 { 9187 // Store locally 9188 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 9189 payload.Data = g.DragDropPayloadBufLocal; 9190 memcpy(payload.Data, data, data_size); 9191 } 9192 else 9193 { 9194 payload.Data = NULL; 9195 } 9196 payload.DataSize = (int)data_size; 9197 } 9198 payload.DataFrameCount = g.FrameCount; 9199 9200 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); 9201} 9202 9203bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) 9204{ 9205 ImGuiContext& g = *GImGui; 9206 if (!g.DragDropActive) 9207 return false; 9208 9209 ImGuiWindow* window = g.CurrentWindow; 9210 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) 9211 return false; 9212 IM_ASSERT(id != 0); 9213 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) 9214 return false; 9215 if (window->SkipItems) 9216 return false; 9217 9218 IM_ASSERT(g.DragDropWithinTarget == false); 9219 g.DragDropTargetRect = bb; 9220 g.DragDropTargetId = id; 9221 g.DragDropWithinTarget = true; 9222 return true; 9223} 9224 9225// We don't use BeginDragDropTargetCustom() and duplicate its code because: 9226// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. 9227// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. 9228// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) 9229bool ImGui::BeginDragDropTarget() 9230{ 9231 ImGuiContext& g = *GImGui; 9232 if (!g.DragDropActive) 9233 return false; 9234 9235 ImGuiWindow* window = g.CurrentWindow; 9236 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 9237 return false; 9238 if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) 9239 return false; 9240 9241 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; 9242 ImGuiID id = window->DC.LastItemId; 9243 if (id == 0) 9244 id = window->GetIDFromRectangle(display_rect); 9245 if (g.DragDropPayload.SourceId == id) 9246 return false; 9247 9248 IM_ASSERT(g.DragDropWithinTarget == false); 9249 g.DragDropTargetRect = display_rect; 9250 g.DragDropTargetId = id; 9251 g.DragDropWithinTarget = true; 9252 return true; 9253} 9254 9255bool ImGui::IsDragDropPayloadBeingAccepted() 9256{ 9257 ImGuiContext& g = *GImGui; 9258 return g.DragDropActive && g.DragDropAcceptIdPrev != 0; 9259} 9260 9261const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) 9262{ 9263 ImGuiContext& g = *GImGui; 9264 ImGuiWindow* window = g.CurrentWindow; 9265 ImGuiPayload& payload = g.DragDropPayload; 9266 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? 9267 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? 9268 if (type != NULL && !payload.IsDataType(type)) 9269 return NULL; 9270 9271 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. 9272 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! 9273 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); 9274 ImRect r = g.DragDropTargetRect; 9275 float r_surface = r.GetWidth() * r.GetHeight(); 9276 if (r_surface < g.DragDropAcceptIdCurrRectSurface) 9277 { 9278 g.DragDropAcceptFlags = flags; 9279 g.DragDropAcceptIdCurr = g.DragDropTargetId; 9280 g.DragDropAcceptIdCurrRectSurface = r_surface; 9281 } 9282 9283 // Render default drop visuals 9284 payload.Preview = was_accepted_previously; 9285 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) 9286 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) 9287 { 9288 // FIXME-DRAG: Settle on a proper default visuals for drop target. 9289 r.Expand(3.5f); 9290 bool push_clip_rect = !window->ClipRect.Contains(r); 9291 if (push_clip_rect) window->DrawList->PushClipRect(r.Min-ImVec2(1,1), r.Max+ImVec2(1,1)); 9292 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); 9293 if (push_clip_rect) window->DrawList->PopClipRect(); 9294 } 9295 9296 g.DragDropAcceptFrameCount = g.FrameCount; 9297 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() 9298 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) 9299 return NULL; 9300 9301 return &payload; 9302} 9303 9304const ImGuiPayload* ImGui::GetDragDropPayload() 9305{ 9306 ImGuiContext& g = *GImGui; 9307 return g.DragDropActive ? &g.DragDropPayload : NULL; 9308} 9309 9310// We don't really use/need this now, but added it for the sake of consistency and because we might need it later. 9311void ImGui::EndDragDropTarget() 9312{ 9313 ImGuiContext& g = *GImGui; 9314 IM_ASSERT(g.DragDropActive); 9315 IM_ASSERT(g.DragDropWithinTarget); 9316 g.DragDropWithinTarget = false; 9317} 9318 9319//----------------------------------------------------------------------------- 9320// [SECTION] LOGGING/CAPTURING 9321//----------------------------------------------------------------------------- 9322// All text output from the interface can be captured into tty/file/clipboard. 9323// By default, tree nodes are automatically opened during logging. 9324//----------------------------------------------------------------------------- 9325 9326// Pass text data straight to log (without being displayed) 9327void ImGui::LogText(const char* fmt, ...) 9328{ 9329 ImGuiContext& g = *GImGui; 9330 if (!g.LogEnabled) 9331 return; 9332 9333 va_list args; 9334 va_start(args, fmt); 9335 if (g.LogFile) 9336 { 9337 g.LogBuffer.Buf.resize(0); 9338 g.LogBuffer.appendfv(fmt, args); 9339 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile); 9340 } 9341 else 9342 { 9343 g.LogBuffer.appendfv(fmt, args); 9344 } 9345 va_end(args); 9346} 9347 9348// Internal version that takes a position to decide on newline placement and pad items according to their depth. 9349// We split text into individual lines to add current tree level padding 9350void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) 9351{ 9352 ImGuiContext& g = *GImGui; 9353 ImGuiWindow* window = g.CurrentWindow; 9354 9355 if (!text_end) 9356 text_end = FindRenderedTextEnd(text, text_end); 9357 9358 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); 9359 if (ref_pos) 9360 g.LogLinePosY = ref_pos->y; 9361 if (log_new_line) 9362 g.LogLineFirstItem = true; 9363 9364 const char* text_remaining = text; 9365 if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth 9366 g.LogDepthRef = window->DC.TreeDepth; 9367 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); 9368 for (;;) 9369 { 9370 // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. 9371 // We don't add a trailing \n to allow a subsequent item on the same line to be captured. 9372 const char* line_start = text_remaining; 9373 const char* line_end = ImStreolRange(line_start, text_end); 9374 const bool is_first_line = (line_start == text); 9375 const bool is_last_line = (line_end == text_end); 9376 if (!is_last_line || (line_start != line_end)) 9377 { 9378 const int char_count = (int)(line_end - line_start); 9379 if (log_new_line || !is_first_line) 9380 LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); 9381 else if (g.LogLineFirstItem) 9382 LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); 9383 else 9384 LogText(" %.*s", char_count, line_start); 9385 g.LogLineFirstItem = false; 9386 } 9387 else if (log_new_line) 9388 { 9389 // An empty "" string at a different Y position should output a carriage return. 9390 LogText(IM_NEWLINE); 9391 break; 9392 } 9393 9394 if (is_last_line) 9395 break; 9396 text_remaining = line_end + 1; 9397 } 9398} 9399 9400// Start logging/capturing text output 9401void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) 9402{ 9403 ImGuiContext& g = *GImGui; 9404 ImGuiWindow* window = g.CurrentWindow; 9405 IM_ASSERT(g.LogEnabled == false); 9406 IM_ASSERT(g.LogFile == NULL); 9407 IM_ASSERT(g.LogBuffer.empty()); 9408 g.LogEnabled = true; 9409 g.LogType = type; 9410 g.LogDepthRef = window->DC.TreeDepth; 9411 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); 9412 g.LogLinePosY = FLT_MAX; 9413 g.LogLineFirstItem = true; 9414} 9415 9416void ImGui::LogToTTY(int auto_open_depth) 9417{ 9418 ImGuiContext& g = *GImGui; 9419 if (g.LogEnabled) 9420 return; 9421 IM_UNUSED(auto_open_depth); 9422#ifndef IMGUI_DISABLE_TTY_FUNCTIONS 9423 LogBegin(ImGuiLogType_TTY, auto_open_depth); 9424 g.LogFile = stdout; 9425#endif 9426} 9427 9428// Start logging/capturing text output to given file 9429void ImGui::LogToFile(int auto_open_depth, const char* filename) 9430{ 9431 ImGuiContext& g = *GImGui; 9432 if (g.LogEnabled) 9433 return; 9434 9435 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still 9436 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. 9437 // By opening the file in binary mode "ab" we have consistent output everywhere. 9438 if (!filename) 9439 filename = g.IO.LogFilename; 9440 if (!filename || !filename[0]) 9441 return; 9442 ImFileHandle f = ImFileOpen(filename, "ab"); 9443 if (!f) 9444 { 9445 IM_ASSERT(0); 9446 return; 9447 } 9448 9449 LogBegin(ImGuiLogType_File, auto_open_depth); 9450 g.LogFile = f; 9451} 9452 9453// Start logging/capturing text output to clipboard 9454void ImGui::LogToClipboard(int auto_open_depth) 9455{ 9456 ImGuiContext& g = *GImGui; 9457 if (g.LogEnabled) 9458 return; 9459 LogBegin(ImGuiLogType_Clipboard, auto_open_depth); 9460} 9461 9462void ImGui::LogToBuffer(int auto_open_depth) 9463{ 9464 ImGuiContext& g = *GImGui; 9465 if (g.LogEnabled) 9466 return; 9467 LogBegin(ImGuiLogType_Buffer, auto_open_depth); 9468} 9469 9470void ImGui::LogFinish() 9471{ 9472 ImGuiContext& g = *GImGui; 9473 if (!g.LogEnabled) 9474 return; 9475 9476 LogText(IM_NEWLINE); 9477 switch (g.LogType) 9478 { 9479 case ImGuiLogType_TTY: 9480#ifndef IMGUI_DISABLE_TTY_FUNCTIONS 9481 fflush(g.LogFile); 9482#endif 9483 break; 9484 case ImGuiLogType_File: 9485 ImFileClose(g.LogFile); 9486 break; 9487 case ImGuiLogType_Buffer: 9488 break; 9489 case ImGuiLogType_Clipboard: 9490 if (!g.LogBuffer.empty()) 9491 SetClipboardText(g.LogBuffer.begin()); 9492 break; 9493 case ImGuiLogType_None: 9494 IM_ASSERT(0); 9495 break; 9496 } 9497 9498 g.LogEnabled = false; 9499 g.LogType = ImGuiLogType_None; 9500 g.LogFile = NULL; 9501 g.LogBuffer.clear(); 9502} 9503 9504// Helper to display logging buttons 9505// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) 9506void ImGui::LogButtons() 9507{ 9508 ImGuiContext& g = *GImGui; 9509 9510 PushID("LogButtons"); 9511#ifndef IMGUI_DISABLE_TTY_FUNCTIONS 9512 const bool log_to_tty = Button("Log To TTY"); SameLine(); 9513#else 9514 const bool log_to_tty = false; 9515#endif 9516 const bool log_to_file = Button("Log To File"); SameLine(); 9517 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); 9518 PushAllowKeyboardFocus(false); 9519 SetNextItemWidth(80.0f); 9520 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); 9521 PopAllowKeyboardFocus(); 9522 PopID(); 9523 9524 // Start logging at the end of the function so that the buttons don't appear in the log 9525 if (log_to_tty) 9526 LogToTTY(); 9527 if (log_to_file) 9528 LogToFile(); 9529 if (log_to_clipboard) 9530 LogToClipboard(); 9531} 9532 9533//----------------------------------------------------------------------------- 9534// [SECTION] SETTINGS 9535//----------------------------------------------------------------------------- 9536 9537// Called by NewFrame() 9538void ImGui::UpdateSettings() 9539{ 9540 // Load settings on first frame (if not explicitly loaded manually before) 9541 ImGuiContext& g = *GImGui; 9542 if (!g.SettingsLoaded) 9543 { 9544 IM_ASSERT(g.SettingsWindows.empty()); 9545 if (g.IO.IniFilename) 9546 LoadIniSettingsFromDisk(g.IO.IniFilename); 9547 g.SettingsLoaded = true; 9548 } 9549 9550 // Save settings (with a delay after the last modification, so we don't spam disk too much) 9551 if (g.SettingsDirtyTimer > 0.0f) 9552 { 9553 g.SettingsDirtyTimer -= g.IO.DeltaTime; 9554 if (g.SettingsDirtyTimer <= 0.0f) 9555 { 9556 if (g.IO.IniFilename != NULL) 9557 SaveIniSettingsToDisk(g.IO.IniFilename); 9558 else 9559 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. 9560 g.SettingsDirtyTimer = 0.0f; 9561 } 9562 } 9563} 9564 9565void ImGui::MarkIniSettingsDirty() 9566{ 9567 ImGuiContext& g = *GImGui; 9568 if (g.SettingsDirtyTimer <= 0.0f) 9569 g.SettingsDirtyTimer = g.IO.IniSavingRate; 9570} 9571 9572void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) 9573{ 9574 ImGuiContext& g = *GImGui; 9575 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) 9576 if (g.SettingsDirtyTimer <= 0.0f) 9577 g.SettingsDirtyTimer = g.IO.IniSavingRate; 9578} 9579 9580ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) 9581{ 9582 ImGuiContext& g = *GImGui; 9583 9584#if !IMGUI_DEBUG_INI_SETTINGS 9585 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() 9586 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. 9587 if (const char* p = strstr(name, "###")) 9588 name = p; 9589#endif 9590 const size_t name_len = strlen(name); 9591 9592 // Allocate chunk 9593 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1; 9594 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size); 9595 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings(); 9596 settings->ID = ImHashStr(name, name_len); 9597 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator 9598 9599 return settings; 9600} 9601 9602ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) 9603{ 9604 ImGuiContext& g = *GImGui; 9605 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 9606 if (settings->ID == id) 9607 return settings; 9608 return NULL; 9609} 9610 9611ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) 9612{ 9613 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name))) 9614 return settings; 9615 return CreateNewWindowSettings(name); 9616} 9617 9618void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) 9619{ 9620 size_t file_data_size = 0; 9621 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); 9622 if (!file_data) 9623 return; 9624 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); 9625 IM_FREE(file_data); 9626} 9627 9628ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) 9629{ 9630 ImGuiContext& g = *GImGui; 9631 const ImGuiID type_hash = ImHashStr(type_name); 9632 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 9633 if (g.SettingsHandlers[handler_n].TypeHash == type_hash) 9634 return &g.SettingsHandlers[handler_n]; 9635 return NULL; 9636} 9637 9638// Zero-tolerance, no error reporting, cheap .ini parsing 9639void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) 9640{ 9641 ImGuiContext& g = *GImGui; 9642 IM_ASSERT(g.Initialized); 9643 IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); 9644 9645 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). 9646 // 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.. 9647 if (ini_size == 0) 9648 ini_size = strlen(ini_data); 9649 char* buf = (char*)IM_ALLOC(ini_size + 1); 9650 char* buf_end = buf + ini_size; 9651 memcpy(buf, ini_data, ini_size); 9652 buf[ini_size] = 0; 9653 9654 void* entry_data = NULL; 9655 ImGuiSettingsHandler* entry_handler = NULL; 9656 9657 char* line_end = NULL; 9658 for (char* line = buf; line < buf_end; line = line_end + 1) 9659 { 9660 // Skip new lines markers, then find end of the line 9661 while (*line == '\n' || *line == '\r') 9662 line++; 9663 line_end = line; 9664 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') 9665 line_end++; 9666 line_end[0] = 0; 9667 if (line[0] == ';') 9668 continue; 9669 if (line[0] == '[' && line_end > line && line_end[-1] == ']') 9670 { 9671 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. 9672 line_end[-1] = 0; 9673 const char* name_end = line_end - 1; 9674 const char* type_start = line + 1; 9675 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']'); 9676 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; 9677 if (!type_end || !name_start) 9678 continue; 9679 *type_end = 0; // Overwrite first ']' 9680 name_start++; // Skip second '[' 9681 entry_handler = FindSettingsHandler(type_start); 9682 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; 9683 } 9684 else if (entry_handler != NULL && entry_data != NULL) 9685 { 9686 // Let type handler parse the line 9687 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); 9688 } 9689 } 9690 IM_FREE(buf); 9691 g.SettingsLoaded = true; 9692} 9693 9694void ImGui::SaveIniSettingsToDisk(const char* ini_filename) 9695{ 9696 ImGuiContext& g = *GImGui; 9697 g.SettingsDirtyTimer = 0.0f; 9698 if (!ini_filename) 9699 return; 9700 9701 size_t ini_data_size = 0; 9702 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); 9703 ImFileHandle f = ImFileOpen(ini_filename, "wt"); 9704 if (!f) 9705 return; 9706 ImFileWrite(ini_data, sizeof(char), ini_data_size, f); 9707 ImFileClose(f); 9708} 9709 9710// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer 9711const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) 9712{ 9713 ImGuiContext& g = *GImGui; 9714 g.SettingsDirtyTimer = 0.0f; 9715 g.SettingsIniData.Buf.resize(0); 9716 g.SettingsIniData.Buf.push_back(0); 9717 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 9718 { 9719 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; 9720 handler->WriteAllFn(&g, handler, &g.SettingsIniData); 9721 } 9722 if (out_size) 9723 *out_size = (size_t)g.SettingsIniData.size(); 9724 return g.SettingsIniData.c_str(); 9725} 9726 9727static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) 9728{ 9729 ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name)); 9730 if (!settings) 9731 settings = ImGui::CreateNewWindowSettings(name); 9732 return (void*)settings; 9733} 9734 9735static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) 9736{ 9737 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; 9738 int x, y; 9739 int i; 9740 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) settings->Pos = ImVec2ih((short)x, (short)y); 9741 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) settings->Size = ImVec2ih((short)x, (short)y); 9742 else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); 9743} 9744 9745static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) 9746{ 9747 // Gather data from windows that were active during this session 9748 // (if a window wasn't opened in this session we preserve its settings) 9749 ImGuiContext& g = *ctx; 9750 for (int i = 0; i != g.Windows.Size; i++) 9751 { 9752 ImGuiWindow* window = g.Windows[i]; 9753 if (window->Flags & ImGuiWindowFlags_NoSavedSettings) 9754 continue; 9755 9756 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID); 9757 if (!settings) 9758 { 9759 settings = ImGui::CreateNewWindowSettings(window->Name); 9760 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); 9761 } 9762 IM_ASSERT(settings->ID == window->ID); 9763 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); 9764 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); 9765 settings->Collapsed = window->Collapsed; 9766 } 9767 9768 // Write to text buffer 9769 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve 9770 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 9771 { 9772 const char* settings_name = settings->GetName(); 9773 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); 9774 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); 9775 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); 9776 buf->appendf("Collapsed=%d\n", settings->Collapsed); 9777 buf->append("\n"); 9778 } 9779} 9780 9781 9782//----------------------------------------------------------------------------- 9783// [SECTION] VIEWPORTS, PLATFORM WINDOWS 9784//----------------------------------------------------------------------------- 9785 9786// (this section is filled in the 'docking' branch) 9787 9788 9789//----------------------------------------------------------------------------- 9790// [SECTION] DOCKING 9791//----------------------------------------------------------------------------- 9792 9793// (this section is filled in the 'docking' branch) 9794 9795 9796//----------------------------------------------------------------------------- 9797// [SECTION] PLATFORM DEPENDENT HELPERS 9798//----------------------------------------------------------------------------- 9799 9800#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) 9801 9802#ifdef _MSC_VER 9803#pragma comment(lib, "user32") 9804#pragma comment(lib, "kernel32") 9805#endif 9806 9807// Win32 clipboard implementation 9808// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() 9809static const char* GetClipboardTextFn_DefaultImpl(void*) 9810{ 9811 ImGuiContext& g = *GImGui; 9812 g.ClipboardHandlerData.clear(); 9813 if (!::OpenClipboard(NULL)) 9814 return NULL; 9815 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); 9816 if (wbuf_handle == NULL) 9817 { 9818 ::CloseClipboard(); 9819 return NULL; 9820 } 9821 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) 9822 { 9823 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); 9824 g.ClipboardHandlerData.resize(buf_len); 9825 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); 9826 } 9827 ::GlobalUnlock(wbuf_handle); 9828 ::CloseClipboard(); 9829 return g.ClipboardHandlerData.Data; 9830} 9831 9832static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 9833{ 9834 if (!::OpenClipboard(NULL)) 9835 return; 9836 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); 9837 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); 9838 if (wbuf_handle == NULL) 9839 { 9840 ::CloseClipboard(); 9841 return; 9842 } 9843 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); 9844 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); 9845 ::GlobalUnlock(wbuf_handle); 9846 ::EmptyClipboard(); 9847 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) 9848 ::GlobalFree(wbuf_handle); 9849 ::CloseClipboard(); 9850} 9851 9852#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) 9853 9854#include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file 9855static PasteboardRef main_clipboard = 0; 9856 9857// OSX clipboard implementation 9858// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! 9859static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 9860{ 9861 if (!main_clipboard) 9862 PasteboardCreate(kPasteboardClipboard, &main_clipboard); 9863 PasteboardClear(main_clipboard); 9864 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); 9865 if (cf_data) 9866 { 9867 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); 9868 CFRelease(cf_data); 9869 } 9870} 9871 9872static const char* GetClipboardTextFn_DefaultImpl(void*) 9873{ 9874 if (!main_clipboard) 9875 PasteboardCreate(kPasteboardClipboard, &main_clipboard); 9876 PasteboardSynchronize(main_clipboard); 9877 9878 ItemCount item_count = 0; 9879 PasteboardGetItemCount(main_clipboard, &item_count); 9880 for (ItemCount i = 0; i < item_count; i++) 9881 { 9882 PasteboardItemID item_id = 0; 9883 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); 9884 CFArrayRef flavor_type_array = 0; 9885 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); 9886 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) 9887 { 9888 CFDataRef cf_data; 9889 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) 9890 { 9891 ImGuiContext& g = *GImGui; 9892 g.ClipboardHandlerData.clear(); 9893 int length = (int)CFDataGetLength(cf_data); 9894 g.ClipboardHandlerData.resize(length + 1); 9895 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); 9896 g.ClipboardHandlerData[length] = 0; 9897 CFRelease(cf_data); 9898 return g.ClipboardHandlerData.Data; 9899 } 9900 } 9901 } 9902 return NULL; 9903} 9904 9905#else 9906 9907// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. 9908static const char* GetClipboardTextFn_DefaultImpl(void*) 9909{ 9910 ImGuiContext& g = *GImGui; 9911 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); 9912} 9913 9914static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 9915{ 9916 ImGuiContext& g = *GImGui; 9917 g.ClipboardHandlerData.clear(); 9918 const char* text_end = text + strlen(text); 9919 g.ClipboardHandlerData.resize((int)(text_end - text) + 1); 9920 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); 9921 g.ClipboardHandlerData[(int)(text_end - text)] = 0; 9922} 9923 9924#endif 9925 9926// Win32 API IME support (for Asian languages, etc.) 9927#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) 9928 9929#include <imm.h> 9930#ifdef _MSC_VER 9931#pragma comment(lib, "imm32") 9932#endif 9933 9934static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) 9935{ 9936 // Notify OS Input Method Editor of text input position 9937 ImGuiIO& io = ImGui::GetIO(); 9938 if (HWND hwnd = (HWND)io.ImeWindowHandle) 9939 if (HIMC himc = ::ImmGetContext(hwnd)) 9940 { 9941 COMPOSITIONFORM cf; 9942 cf.ptCurrentPos.x = x; 9943 cf.ptCurrentPos.y = y; 9944 cf.dwStyle = CFS_FORCE_POSITION; 9945 ::ImmSetCompositionWindow(himc, &cf); 9946 ::ImmReleaseContext(hwnd, himc); 9947 } 9948} 9949 9950#else 9951 9952static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} 9953 9954#endif 9955 9956//----------------------------------------------------------------------------- 9957// [SECTION] METRICS/DEBUG WINDOW 9958//----------------------------------------------------------------------------- 9959 9960#ifndef IMGUI_DISABLE_METRICS_WINDOW 9961// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. 9962static void MetricsHelpMarker(const char* desc) 9963{ 9964 ImGui::TextDisabled("(?)"); 9965 if (ImGui::IsItemHovered()) 9966 { 9967 ImGui::BeginTooltip(); 9968 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 9969 ImGui::TextUnformatted(desc); 9970 ImGui::PopTextWrapPos(); 9971 ImGui::EndTooltip(); 9972 } 9973} 9974 9975void ImGui::ShowMetricsWindow(bool* p_open) 9976{ 9977 if (!ImGui::Begin("Dear ImGui Metrics", p_open)) 9978 { 9979 ImGui::End(); 9980 return; 9981 } 9982 9983 // Debugging enums 9984 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type 9985 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" }; 9986 enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersDesired, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type 9987 const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersDesired", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" }; 9988 9989 // State 9990 static bool show_windows_rects = false; 9991 static int show_windows_rect_type = WRT_WorkRect; 9992 static bool show_windows_begin_order = false; 9993 static bool show_tables_rects = false; 9994 static int show_tables_rect_type = TRT_WorkRect; 9995 static bool show_drawcmd_mesh = true; 9996 static bool show_drawcmd_aabb = true; 9997 9998 // Basic info 9999 ImGuiContext& g = *GImGui; 10000 ImGuiIO& io = ImGui::GetIO(); 10001 ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); 10002 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); 10003 ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); 10004 ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); 10005 ImGui::Text("%d active allocations", io.MetricsActiveAllocations); 10006 ImGui::Separator(); 10007 10008 // Helper functions to display common structures: 10009 // - NodeDrawList() 10010 // - NodeColumns() 10011 // - NodeWindow() 10012 // - NodeWindows() 10013 // - NodeTabBar() 10014 // - NodeStorage() 10015 struct Funcs 10016 { 10017 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) 10018 { 10019 if (rect_type == WRT_OuterRect) { return window->Rect(); } 10020 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; } 10021 else if (rect_type == WRT_InnerRect) { return window->InnerRect; } 10022 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } 10023 else if (rect_type == WRT_WorkRect) { return window->WorkRect; } 10024 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } 10025 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } 10026 IM_ASSERT(0); 10027 return ImRect(); 10028 } 10029 10030 static void NodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb) 10031 { 10032 IM_ASSERT(show_mesh || show_aabb); 10033 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list 10034 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 10035 10036 // Draw wire-frame version of all triangles 10037 ImRect clip_rect = draw_cmd->ClipRect; 10038 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); 10039 ImDrawListFlags backup_flags = fg_draw_list->Flags; 10040 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. 10041 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + draw_cmd->ElemCount); base_idx += 3) 10042 { 10043 ImVec2 triangle[3]; 10044 for (int n = 0; n < 3; n++) 10045 { 10046 ImVec2 p = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos; 10047 triangle[n] = p; 10048 vtxs_rect.Add(p); 10049 } 10050 if (show_mesh) 10051 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), true, 1.0f); // In yellow: mesh triangles 10052 } 10053 // Draw bounding boxes 10054 if (show_aabb) 10055 { 10056 fg_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU 10057 fg_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles 10058 } 10059 fg_draw_list->Flags = backup_flags; 10060 } 10061 10062 static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) 10063 { 10064 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); 10065 if (draw_list == ImGui::GetWindowDrawList()) 10066 { 10067 ImGui::SameLine(); 10068 ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) 10069 if (node_open) ImGui::TreePop(); 10070 return; 10071 } 10072 10073 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list 10074 if (window && IsItemHovered()) 10075 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); 10076 if (!node_open) 10077 return; 10078 10079 if (window && !window->WasActive) 10080 ImGui::TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!"); 10081 10082 unsigned int elem_offset = 0; 10083 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) 10084 { 10085 if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) 10086 continue; 10087 if (pcmd->UserCallback) 10088 { 10089 ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); 10090 continue; 10091 } 10092 10093 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 10094 char buf[300]; 10095 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd: %4d triangles, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", 10096 pcmd->ElemCount/3, (void*)(intptr_t)pcmd->TextureId, 10097 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); 10098 bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); 10099 if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list) 10100 NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb); 10101 if (!pcmd_node_open) 10102 continue; 10103 10104 // Calculate approximate coverage area (touched pixel count) 10105 // This will be in pixels squared as long there's no post-scaling happening to the renderer output. 10106 float total_area = 0.0f; 10107 for (unsigned int base_idx = elem_offset; base_idx < (elem_offset + pcmd->ElemCount); base_idx += 3) 10108 { 10109 ImVec2 triangle[3]; 10110 for (int n = 0; n < 3; n++) 10111 triangle[n] = draw_list->VtxBuffer[idx_buffer ? idx_buffer[base_idx + n] : (base_idx + n)].pos; 10112 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]); 10113 } 10114 10115 // Display vertex information summary. Hover to get all triangles drawn in wire-frame 10116 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); 10117 ImGui::Selectable(buf); 10118 if (ImGui::IsItemHovered() && fg_draw_list) 10119 NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false); 10120 10121 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. 10122 ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. 10123 while (clipper.Step()) 10124 for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) 10125 { 10126 char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); 10127 ImVec2 triangle[3]; 10128 for (int n = 0; n < 3; n++, idx_i++) 10129 { 10130 ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[idx_i] : idx_i]; 10131 triangle[n] = v.pos; 10132 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", 10133 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); 10134 } 10135 10136 ImGui::Selectable(buf, false); 10137 if (fg_draw_list && ImGui::IsItemHovered()) 10138 { 10139 ImDrawListFlags backup_flags = fg_draw_list->Flags; 10140 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. 10141 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255,255,0,255), true, 1.0f); 10142 fg_draw_list->Flags = backup_flags; 10143 } 10144 } 10145 ImGui::TreePop(); 10146 } 10147 ImGui::TreePop(); 10148 } 10149 10150 static void NodeColumns(const ImGuiColumns* columns) 10151 { 10152 if (!ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) 10153 return; 10154 ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); 10155 for (int column_n = 0; column_n < columns->Columns.Size; column_n++) 10156 ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm)); 10157 ImGui::TreePop(); 10158 } 10159 10160 static void NodeWindows(ImVector<ImGuiWindow*>& windows, const char* label) 10161 { 10162 if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) 10163 return; 10164 for (int i = 0; i < windows.Size; i++) 10165 { 10166 ImGui::PushID(windows[i]); 10167 Funcs::NodeWindow(windows[i], "Window"); 10168 ImGui::PopID(); 10169 } 10170 ImGui::TreePop(); 10171 } 10172 10173 static void NodeWindow(ImGuiWindow* window, const char* label) 10174 { 10175 if (window == NULL) 10176 { 10177 ImGui::BulletText("%s: NULL", label); 10178 return; 10179 } 10180 bool open = ImGui::TreeNode(label, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window); 10181 if (ImGui::IsItemHovered() && window->WasActive) 10182 ImGui::GetForegroundDrawList()->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); 10183 if (!open) 10184 return; 10185 10186 if (!window->WasActive) 10187 ImGui::TextDisabled("Note: window is not currently visible."); 10188 if (window->MemoryCompacted) 10189 ImGui::TextDisabled("Note: some memory buffers have been compacted/freed."); 10190 10191 ImGuiWindowFlags flags = window->Flags; 10192 NodeDrawList(window, window->DrawList, "DrawList"); 10193 ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y); 10194 ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, 10195 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", 10196 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", 10197 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); 10198 ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); 10199 ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); 10200 ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); 10201 ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); 10202 ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); 10203 if (!window->NavRectRel[0].IsInverted()) 10204 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); 10205 else 10206 ImGui::BulletText("NavRectRel[0]: <None>"); 10207 if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); 10208 if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); 10209 if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); 10210 if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) 10211 { 10212 for (int n = 0; n < window->ColumnsStorage.Size; n++) 10213 NodeColumns(&window->ColumnsStorage[n]); 10214 ImGui::TreePop(); 10215 } 10216 NodeStorage(&window->StateStorage, "Storage"); 10217 ImGui::TreePop(); 10218 } 10219 10220 static void NodeTabBar(ImGuiTabBar* tab_bar) 10221 { 10222 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. 10223 char buf[256]; 10224 char* p = buf; 10225 const char* buf_end = buf + IM_ARRAYSIZE(buf); 10226 p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); 10227 if (ImGui::TreeNode(tab_bar, "%s", buf)) 10228 { 10229 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) 10230 { 10231 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; 10232 ImGui::PushID(tab); 10233 if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); 10234 if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); 10235 ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : ""); 10236 ImGui::PopID(); 10237 } 10238 ImGui::TreePop(); 10239 } 10240 } 10241 10242 static void NodeStorage(ImGuiStorage* storage, const char* label) 10243 { 10244 if (!ImGui::TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes())) 10245 return; 10246 for (int n = 0; n < storage->Data.Size; n++) 10247 { 10248 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n]; 10249 ImGui::BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer. 10250 } 10251 ImGui::TreePop(); 10252 } 10253 }; 10254 10255 10256 // Tools 10257 if (ImGui::TreeNode("Tools")) 10258 { 10259 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. 10260 if (ImGui::Button("Item Picker..")) 10261 ImGui::DebugStartItemPicker(); 10262 ImGui::SameLine(); 10263 MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); 10264 10265 ImGui::Checkbox("Show windows begin order", &show_windows_begin_order); 10266 ImGui::Checkbox("Show windows rectangles", &show_windows_rects); 10267 ImGui::SameLine(); 10268 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12); 10269 show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count); 10270 if (show_windows_rects && g.NavWindow) 10271 { 10272 ImGui::BulletText("'%s':", g.NavWindow->Name); 10273 ImGui::Indent(); 10274 for (int rect_n = 0; rect_n < WRT_Count; rect_n++) 10275 { 10276 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n); 10277 ImGui::Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]); 10278 } 10279 ImGui::Unindent(); 10280 } 10281 ImGui::Checkbox("Show mesh when hovering ImDrawCmd", &show_drawcmd_mesh); 10282 ImGui::Checkbox("Show bounding boxes when hovering ImDrawCmd", &show_drawcmd_aabb); 10283 ImGui::TreePop(); 10284 } 10285 10286 // Contents 10287 Funcs::NodeWindows(g.Windows, "Windows"); 10288 //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder"); 10289 if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) 10290 { 10291 for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) 10292 Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); 10293 ImGui::TreePop(); 10294 } 10295 10296 // Details for Popups 10297 if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) 10298 { 10299 for (int i = 0; i < g.OpenPopupStack.Size; i++) 10300 { 10301 ImGuiWindow* window = g.OpenPopupStack[i].Window; 10302 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" : ""); 10303 } 10304 ImGui::TreePop(); 10305 } 10306 10307 // Details for TabBars 10308 if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) 10309 { 10310 for (int n = 0; n < g.TabBars.GetSize(); n++) 10311 Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); 10312 ImGui::TreePop(); 10313 } 10314 10315 // Details for Tables 10316 IM_UNUSED(trt_rects_names); 10317 IM_UNUSED(show_tables_rects); 10318 IM_UNUSED(show_tables_rect_type); 10319#ifdef IMGUI_HAS_TABLE 10320 if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) 10321 { 10322 for (int n = 0; n < g.Tables.GetSize(); n++) 10323 Funcs::NodeTable(g.Tables.GetByIndex(n)); 10324 ImGui::TreePop(); 10325 } 10326#endif // #define IMGUI_HAS_TABLE 10327 10328 // Details for Docking 10329#ifdef IMGUI_HAS_DOCK 10330 if (ImGui::TreeNode("Docking")) 10331 { 10332 ImGui::TreePop(); 10333 } 10334#endif // #define IMGUI_HAS_DOCK 10335 10336 // Misc Details 10337 if (ImGui::TreeNode("Internal state")) 10338 { 10339 const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); 10340 ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); 10341 ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); 10342 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 10343 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]); 10344 ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); 10345 ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); 10346 ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); 10347 ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); 10348 ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); 10349 ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); 10350 ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); 10351 ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); 10352 ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); 10353 ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); 10354 ImGui::TreePop(); 10355 } 10356 10357 // Overlay: Display windows Rectangles and Begin Order 10358 if (show_windows_rects || show_windows_begin_order) 10359 { 10360 for (int n = 0; n < g.Windows.Size; n++) 10361 { 10362 ImGuiWindow* window = g.Windows[n]; 10363 if (!window->WasActive) 10364 continue; 10365 ImDrawList* draw_list = GetForegroundDrawList(window); 10366 if (show_windows_rects) 10367 { 10368 ImRect r = Funcs::GetWindowRect(window, show_windows_rect_type); 10369 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); 10370 } 10371 if (show_windows_begin_order && !(window->Flags & ImGuiWindowFlags_ChildWindow)) 10372 { 10373 char buf[32]; 10374 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); 10375 float font_size = ImGui::GetFontSize(); 10376 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); 10377 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); 10378 } 10379 } 10380 } 10381 10382#ifdef IMGUI_HAS_TABLE 10383 // Overlay: Display Tables Rectangles 10384 if (show_tables_rects) 10385 { 10386 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) 10387 { 10388 ImGuiTable* table = g.Tables.GetByIndex(table_n); 10389 } 10390 } 10391#endif // #define IMGUI_HAS_TABLE 10392 10393#ifdef IMGUI_HAS_DOCK 10394 // Overlay: Display Docking info 10395 if (show_docking_nodes && g.IO.KeyCtrl) 10396 { 10397 } 10398#endif // #define IMGUI_HAS_DOCK 10399 10400 ImGui::End(); 10401} 10402 10403#else 10404 10405void ImGui::ShowMetricsWindow(bool*) { } 10406 10407#endif 10408 10409//----------------------------------------------------------------------------- 10410 10411// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. 10412// 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. 10413#ifdef IMGUI_INCLUDE_IMGUI_USER_INL 10414#include "imgui_user.inl" 10415#endif 10416 10417//----------------------------------------------------------------------------- 10418 10419#endif // #ifndef IMGUI_DISABLE