imgui.cpp (570555B)
1 // dear imgui, v1.83 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. 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/3793 (please post your screenshots/video there!) 15 // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) 16 // - Glossary https://github.com/ocornut/imgui/wiki/Glossary 17 // - Issues & support https://github.com/ocornut/imgui/issues 18 // - Discussions https://github.com/ocornut/imgui/discussions 19 20 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. 21 // See LICENSE.txt for copyright and licensing details (standard MIT License). 22 // This library is free but needs your support to sustain development and maintenance. 23 // Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com". 24 // Individuals: you can support continued development via donations. See docs/README or web page. 25 26 // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. 27 // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without 28 // modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't 29 // come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you 30 // to a better solution or official support for them. 31 32 /* 33 34 Index of this file: 35 36 DOCUMENTATION 37 38 - MISSION STATEMENT 39 - END-USER GUIDE 40 - PROGRAMMER GUIDE 41 - READ FIRST 42 - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 43 - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 44 - HOW A SIMPLE APPLICATION MAY LOOK LIKE 45 - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE 46 - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 47 - API BREAKING CHANGES (read me when you update!) 48 - FREQUENTLY ASKED QUESTIONS (FAQ) 49 - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer) 50 51 CODE 52 (search for "[SECTION]" in the code to find them) 53 54 // [SECTION] INCLUDES 55 // [SECTION] FORWARD DECLARATIONS 56 // [SECTION] CONTEXT AND MEMORY ALLOCATORS 57 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) 58 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) 59 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) 60 // [SECTION] MISC HELPERS/UTILITIES (File functions) 61 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions) 62 // [SECTION] MISC HELPERS/UTILITIES (Color functions) 63 // [SECTION] ImGuiStorage 64 // [SECTION] ImGuiTextFilter 65 // [SECTION] ImGuiTextBuffer 66 // [SECTION] ImGuiListClipper 67 // [SECTION] STYLING 68 // [SECTION] RENDER HELPERS 69 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) 70 // [SECTION] ERROR CHECKING 71 // [SECTION] LAYOUT 72 // [SECTION] SCROLLING 73 // [SECTION] TOOLTIPS 74 // [SECTION] POPUPS 75 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION 76 // [SECTION] DRAG AND DROP 77 // [SECTION] LOGGING/CAPTURING 78 // [SECTION] SETTINGS 79 // [SECTION] VIEWPORTS 80 // [SECTION] PLATFORM DEPENDENT HELPERS 81 // [SECTION] METRICS/DEBUGGER WINDOW 82 83 */ 84 85 //----------------------------------------------------------------------------- 86 // DOCUMENTATION 87 //----------------------------------------------------------------------------- 88 89 /* 90 91 MISSION STATEMENT 92 ================= 93 94 - Easy to use to create code-driven and data-driven tools. 95 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. 96 - Easy to hack and improve. 97 - Minimize setup and maintenance. 98 - Minimize state storage on user side. 99 - Portable, minimize dependencies, run on target (consoles, phones, etc.). 100 - Efficient runtime and memory consumption. 101 102 Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes: 103 104 - Doesn't look fancy, doesn't animate. 105 - Limited layout features, intricate layouts are typically crafted in code. 106 107 108 END-USER GUIDE 109 ============== 110 111 - Double-click on title bar to collapse window. 112 - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin(). 113 - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents). 114 - Click and drag on any empty space to move window. 115 - TAB/SHIFT+TAB to cycle through keyboard editable fields. 116 - CTRL+Click on a slider or drag box to input value as text. 117 - Use mouse wheel to scroll. 118 - Text editor: 119 - Hold SHIFT or use mouse to select text. 120 - CTRL+Left/Right to word jump. 121 - CTRL+Shift+Left/Right to select words. 122 - CTRL+A our Double-Click to select all. 123 - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ 124 - CTRL+Z,CTRL+Y to undo/redo. 125 - ESCAPE to revert text to its original value. 126 - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) 127 - Controls are automatically adjusted for OSX to match standard OSX text editing operations. 128 - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. 129 - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets 130 131 132 PROGRAMMER GUIDE 133 ================ 134 135 READ FIRST 136 ---------- 137 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) 138 - 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 or 139 destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. 140 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. 141 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. 142 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). 143 You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki. 144 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. 145 For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI, 146 where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. 147 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. 148 - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. 149 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). 150 If you get an assert, read the messages and comments around the assert. 151 - 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. 152 - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. 153 See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. 154 However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. 155 - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). 156 157 158 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI 159 ---------------------------------------------- 160 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) 161 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". 162 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. 163 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. 164 If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed 165 from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will 166 likely be a comment about it. Please report any issue to the GitHub page! 167 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file. 168 - Try to keep your copy of Dear ImGui reasonably up to date. 169 170 171 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE 172 --------------------------------------------------------------- 173 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. 174 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. 175 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. 176 It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL). 177 - 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. 178 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. 179 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. 180 Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" 181 phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render(). 182 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code. 183 - 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. 184 185 186 HOW A SIMPLE APPLICATION MAY LOOK LIKE 187 -------------------------------------- 188 EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder). 189 The sub-folders in examples/ contain examples applications following this structure. 190 191 // Application init: create a dear imgui context, setup some options, load fonts 192 ImGui::CreateContext(); 193 ImGuiIO& io = ImGui::GetIO(); 194 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. 195 // TODO: Fill optional fields of the io structure later. 196 // TODO: Load TTF/OTF fonts if you don't want to use the default font. 197 198 // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp) 199 ImGui_ImplWin32_Init(hwnd); 200 ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); 201 202 // Application main loop 203 while (true) 204 { 205 // Feed inputs to dear imgui, start new frame 206 ImGui_ImplDX11_NewFrame(); 207 ImGui_ImplWin32_NewFrame(); 208 ImGui::NewFrame(); 209 210 // Any application code here 211 ImGui::Text("Hello, world!"); 212 213 // Render dear imgui into screen 214 ImGui::Render(); 215 ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); 216 g_pSwapChain->Present(1, 0); 217 } 218 219 // Shutdown 220 ImGui_ImplDX11_Shutdown(); 221 ImGui_ImplWin32_Shutdown(); 222 ImGui::DestroyContext(); 223 224 EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE 225 226 // Application init: create a dear imgui context, setup some options, load fonts 227 ImGui::CreateContext(); 228 ImGuiIO& io = ImGui::GetIO(); 229 // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. 230 // TODO: Fill optional fields of the io structure later. 231 // TODO: Load TTF/OTF fonts if you don't want to use the default font. 232 233 // Build and load the texture atlas into a texture 234 // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer) 235 int width, height; 236 unsigned char* pixels = NULL; 237 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 238 239 // At this point you've got the texture data and you need to upload that to your graphic system: 240 // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'. 241 // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID. 242 MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) 243 io.Fonts->SetTexID((void*)texture); 244 245 // Application main loop 246 while (true) 247 { 248 // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc. 249 // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends) 250 io.DeltaTime = 1.0f/60.0f; // set the time elapsed since the previous frame (in seconds) 251 io.DisplaySize.x = 1920.0f; // set the current display width 252 io.DisplaySize.y = 1280.0f; // set the current display height here 253 io.MousePos = my_mouse_pos; // set the mouse position 254 io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states 255 io.MouseDown[1] = my_mouse_buttons[1]; 256 257 // Call NewFrame(), after this point you can use ImGui::* functions anytime 258 // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere) 259 ImGui::NewFrame(); 260 261 // Most of your application code here 262 ImGui::Text("Hello, world!"); 263 MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); 264 MyGameRender(); // may use any Dear ImGui functions as well! 265 266 // Render dear imgui, swap buffers 267 // (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) 268 ImGui::EndFrame(); 269 ImGui::Render(); 270 ImDrawData* draw_data = ImGui::GetDrawData(); 271 MyImGuiRenderFunction(draw_data); 272 SwapBuffers(); 273 } 274 275 // Shutdown 276 ImGui::DestroyContext(); 277 278 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, 279 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! 280 Please read the FAQ and example applications for details about this! 281 282 283 HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE 284 --------------------------------------------- 285 The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function. 286 287 void void MyImGuiRenderFunction(ImDrawData* draw_data) 288 { 289 // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled 290 // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize 291 // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize 292 // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. 293 for (int n = 0; n < draw_data->CmdListsCount; n++) 294 { 295 const ImDrawList* cmd_list = draw_data->CmdLists[n]; 296 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by Dear ImGui 297 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by Dear ImGui 298 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 299 { 300 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 301 if (pcmd->UserCallback) 302 { 303 pcmd->UserCallback(cmd_list, pcmd); 304 } 305 else 306 { 307 // The texture for the draw call is specified by pcmd->GetTexID(). 308 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. 309 MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); 310 311 // We are using scissoring to clip some objects. All low-level graphics API should support it. 312 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches 313 // (some elements visible outside their bounds) but you can fix that once everything else works! 314 // - Clipping coordinates are provided in imgui coordinates space: 315 // - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size 316 // - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values. 317 // - In the interest of supporting multi-viewport applications (see 'docking' branch on github), 318 // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. 319 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) 320 ImVec2 pos = draw_data->DisplayPos; 321 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)); 322 323 // Render 'pcmd->ElemCount/3' indexed triangles. 324 // 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. 325 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); 326 } 327 idx_buffer += pcmd->ElemCount; 328 } 329 } 330 } 331 332 333 USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS 334 ------------------------------------------ 335 - The gamepad/keyboard navigation is fairly functional and keeps being improved. 336 - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse! 337 - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 338 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. 339 - Keyboard: 340 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. 341 NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. 342 - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag 343 will be set. For more advanced uses, you may want to read from: 344 - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. 345 - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). 346 - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. 347 Please reach out if you think the game vs navigation input sharing could be improved. 348 - Gamepad: 349 - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. 350 - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). 351 Note that io.NavInputs[] is cleared by EndFrame(). 352 - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 353 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. 354 - We use a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. 355 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.). 356 - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets 357 - 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 358 to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. 359 - Mouse: 360 - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. 361 - 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. 362 - 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. 363 Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. 364 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. 365 When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that. 366 (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse moving back and forth!) 367 (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 368 to set a boolean to ignore your other external mouse positions until the external source is moved again.) 369 370 371 API BREAKING CHANGES 372 ==================== 373 374 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. 375 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. 376 When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. 377 You can read releases logs https://github.com/ocornut/imgui/releases for more details. 378 379 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). 380 - if you are using official backends from the source tree: you have nothing to do. 381 - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). 382 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. 383 - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft 384 - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight 385 - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc. 386 flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API. 387 breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners": 388 - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use) 389 - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use) 390 - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use) 391 - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f. 392 this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok. 393 the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts. 394 legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise). 395 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018): 396 - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() 397 - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. 398 - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. 399 - 2021/02/22 (1.82) - win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. 400 - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. 401 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). 402 - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). 403 - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete). 404 - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags. 405 - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags. 406 - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. 407 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018): 408 - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit(). 409 - ImGuiCol_ModalWindowDarkening -> use ImGuiCol_ModalWindowDimBg 410 - ImGuiInputTextCallback -> use ImGuiTextEditCallback 411 - ImGuiInputTextCallbackData -> use ImGuiTextEditCallbackData 412 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete). 413 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added! 414 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API. 415 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures 416 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/. 417 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018): 418 - io.RenderDrawListsFn pointer -> use ImGui::GetDrawData() value and call the render function of your backend 419 - ImGui::IsAnyWindowFocused() -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow) 420 - ImGui::IsAnyWindowHovered() -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow) 421 - ImGuiStyleVar_Count_ -> use ImGuiStyleVar_COUNT 422 - ImGuiMouseCursor_Count_ -> use ImGuiMouseCursor_COUNT 423 - removed redirecting functions names that were marked obsolete in 1.61 (May 2018): 424 - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision. 425 - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter. 426 - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed). 427 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently). 428 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton. 429 - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory. 430 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result. 431 - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now! 432 - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar(). 433 replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags). 434 worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions: 435 - if you omitted the 'power' parameter (likely!), you are not affected. 436 - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct. 437 - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f. 438 see https://github.com/ocornut/imgui/issues/3361 for all details. 439 kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used. 440 for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. 441 - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime. 442 - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems. 443 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79] 444 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017. 445 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular(). 446 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more. 447 - 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. 448 - 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. 449 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017): 450 - ShowTestWindow() -> use ShowDemoWindow() 451 - IsRootWindowFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow) 452 - IsRootWindowOrAnyChildFocused() -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) 453 - SetNextWindowContentWidth(w) -> use SetNextWindowContentSize(ImVec2(w, 0.0f) 454 - GetItemsLineHeightWithSpacing() -> use GetFrameHeightWithSpacing() 455 - ImGuiCol_ChildWindowBg -> use ImGuiCol_ChildBg 456 - ImGuiStyleVar_ChildWindowRounding -> use ImGuiStyleVar_ChildRounding 457 - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap 458 - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS 459 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API. 460 - 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). 461 - 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. 462 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. 463 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency. 464 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017): 465 - Begin() [old 5 args version] -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed 466 - IsRootWindowOrAnyChildHovered() -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) 467 - AlignFirstTextHeightToWidgets() -> use AlignTextToFramePadding() 468 - SetNextWindowPosCenter() -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f) 469 - ImFont::Glyph -> use ImFontGlyph 470 - 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. 471 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. 472 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). 473 If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you. 474 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete). 475 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete). 476 - 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. 477 - 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 478 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. 479 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. 480 Please reach out if you are affected. 481 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). 482 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c). 483 - 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. 484 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete). 485 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). 486 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). 487 - 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 an arbitrarily small value! 488 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). 489 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! 490 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). 491 - 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. 492 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. 493 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. 494 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). 495 - 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. 496 If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. 497 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) 498 - 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. 499 NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED. 500 Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions. 501 - 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). 502 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). 503 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). 504 - 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. 505 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. 506 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. 507 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). 508 - 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.). 509 old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports. 510 when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call. 511 in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function. 512 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. 513 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. 514 - 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. 515 If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. 516 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. 517 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. 518 - 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", 519 consistent with other functions. Kept redirection functions (will obsolete). 520 - 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. 521 - 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 backend ahead of merging the Nav branch). 522 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. 523 - 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. 524 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. 525 - 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. 526 - 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. 527 - 2018/02/07 (1.60) - reorganized context handling to be more explicit, 528 - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. 529 - removed Shutdown() function, as DestroyContext() serve this purpose. 530 - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. 531 - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. 532 - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. 533 - 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. 534 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). 535 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). 536 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. 537 - 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. 538 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). 539 - 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 540 - 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. 541 - 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. 542 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). 543 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). 544 - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). 545 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). 546 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). 547 - 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. 548 - 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. 549 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. 550 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. 551 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. 552 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. 553 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); 554 - 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. 555 - 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. 556 - 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. 557 removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. 558 IsItemHoveredRect() --> IsItemHovered(ImGuiHoveredFlags_RectOnly) 559 IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow) 560 IsMouseHoveringWindow() --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior] 561 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! 562 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). 563 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete). 564 - 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). 565 - 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 backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". 566 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! 567 - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). 568 - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). 569 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. 570 - 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. 571 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type. 572 - 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. 573 - 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). 574 - 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). 575 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). 576 - 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. 577 - 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. 578 - 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))' 579 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse 580 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. 581 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. 582 - 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(). 583 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. 584 - 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. 585 - 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. 586 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. 587 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 to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. 588 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: 589 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); } 590 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. 591 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). 592 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. 593 - 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). 594 - 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. 595 - 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). 596 - 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) 597 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). 598 - 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. 599 - 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. 600 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. 601 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. 602 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. 603 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. 604 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! 605 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize 606 - 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. 607 - 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 608 - 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. 609 you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. 610 - 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. 611 this necessary change will break your rendering function! the fix should be very easy. sorry for that :( 612 - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. 613 - the signature of the io.RenderDrawListsFn handler has changed! 614 old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) 615 new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). 616 parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' 617 ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. 618 ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. 619 - 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. 620 - 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! 621 - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! 622 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. 623 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). 624 - 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. 625 - 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 626 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry! 627 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). 628 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). 629 - 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. 630 - 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. 631 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). 632 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. 633 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API 634 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. 635 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. 636 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. 637 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing 638 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. 639 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) 640 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. 641 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. 642 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. 643 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior 644 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() 645 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) 646 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. 647 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. 648 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. 649 - old: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..]; 650 - new: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier); 651 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. 652 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID() 653 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) 654 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets 655 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) 656 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) 657 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility 658 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() 659 - 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) 660 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) 661 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() 662 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn 663 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) 664 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite 665 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes 666 667 668 FREQUENTLY ASKED QUESTIONS (FAQ) 669 ================================ 670 671 Read all answers online: 672 https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url) 673 Read all answers locally (with a text editor or ideally a Markdown viewer): 674 docs/FAQ.md 675 Some answers are copied down here to facilitate searching in code. 676 677 Q&A: Basics 678 =========== 679 680 Q: Where is the documentation? 681 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. 682 - Run the examples/ and explore them. 683 - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. 684 - The demo covers most features of Dear ImGui, so you can read the code and see its output. 685 - See documentation and comments at the top of imgui.cpp + effectively imgui.h. 686 - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the 687 examples/ folder to explain how to integrate Dear ImGui with your own engine/application. 688 - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. 689 - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. 690 - Your programming IDE is your friend, find the type or function declaration to find comments 691 associated with it. 692 693 Q: What is this library called? 694 Q: Which version should I get? 695 >> This library is called "Dear ImGui", please don't call it "ImGui" :) 696 >> See https://www.dearimgui.org/faq for details. 697 698 Q&A: Integration 699 ================ 700 701 Q: How to get started? 702 A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. 703 704 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? 705 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! 706 >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this. 707 708 Q. How can I enable keyboard controls? 709 Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) 710 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... 711 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... 712 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... 713 >> See https://www.dearimgui.org/faq 714 715 Q&A: Usage 716 ---------- 717 718 Q: Why is my widget not reacting when I click on it? 719 Q: How can I have widgets with an empty label? 720 Q: How can I have multiple widgets with the same label? 721 Q: How can I display an image? What is ImTextureID, how does it works? 722 Q: How can I use my own math types instead of ImVec2/ImVec4? 723 Q: How can I interact with standard C++ types (such as std::string and std::vector)? 724 Q: How can I display custom shapes? (using low-level ImDrawList API) 725 >> See https://www.dearimgui.org/faq 726 727 Q&A: Fonts, Text 728 ================ 729 730 Q: How should I handle DPI in my application? 731 Q: How can I load a different font than the default? 732 Q: How can I easily use icons in my application? 733 Q: How can I load multiple fonts? 734 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? 735 >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md 736 737 Q&A: Concerns 738 ============= 739 740 Q: Who uses Dear ImGui? 741 Q: Can you create elaborate/serious tools with Dear ImGui? 742 Q: Can you reskin the look of Dear ImGui? 743 Q: Why using C++ (as opposed to C)? 744 >> See https://www.dearimgui.org/faq 745 746 Q&A: Community 747 ============== 748 749 Q: How can I help? 750 A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui! 751 We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. 752 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. 753 - Individuals: you can support continued development via PayPal donations. See README. 754 - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt 755 and see how you want to help and can help! 756 - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. 757 You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers. 758 But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions. 759 - 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). 760 761 */ 762 763 //------------------------------------------------------------------------- 764 // [SECTION] INCLUDES 765 //------------------------------------------------------------------------- 766 767 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 768 #define _CRT_SECURE_NO_WARNINGS 769 #endif 770 771 #include "imgui.h" 772 #ifndef IMGUI_DISABLE 773 774 #ifndef IMGUI_DEFINE_MATH_OPERATORS 775 #define IMGUI_DEFINE_MATH_OPERATORS 776 #endif 777 #include "imgui_internal.h" 778 779 // System includes 780 #include <ctype.h> // toupper 781 #include <stdio.h> // vsnprintf, sscanf, printf 782 #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier 783 #include <stddef.h> // intptr_t 784 #else 785 #include <stdint.h> // intptr_t 786 #endif 787 788 // [Windows] OS specific includes (optional) 789 #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) 790 #define IMGUI_DISABLE_WIN32_FUNCTIONS 791 #endif 792 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) 793 #ifndef WIN32_LEAN_AND_MEAN 794 #define WIN32_LEAN_AND_MEAN 795 #endif 796 #ifndef NOMINMAX 797 #define NOMINMAX 798 #endif 799 #include <windows.h> 800 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions 801 #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS 802 #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS 803 #endif 804 #endif 805 806 // [Apple] OS specific includes 807 #if defined(__APPLE__) 808 #include <TargetConditionals.h> 809 #endif 810 811 // Visual Studio warnings 812 #ifdef _MSC_VER 813 #pragma warning (disable: 4127) // condition expression is constant 814 #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 815 #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later 816 #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types 817 #endif 818 #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). 819 #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). 820 #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). 821 #endif 822 823 // Clang/GCC warnings with -Weverything 824 #if defined(__clang__) 825 #if __has_warning("-Wunknown-warning-option") 826 #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! 827 #endif 828 #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' 829 #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. 830 #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. 831 #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. 832 #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. 833 #pragma clang diagnostic ignored "-Wglobal-constructors" // warning: declaration requires a global destructor // similar to above, not sure what the exact difference is. 834 #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness 835 #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. 836 #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning: cast to 'void *' from smaller integer type 'int' 837 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 838 #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. 839 #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision 840 #elif defined(__GNUC__) 841 // We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association. 842 #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind 843 #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used 844 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size 845 #pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' 846 #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function 847 #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value 848 #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked 849 #pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false 850 #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead 851 #endif 852 853 // Debug options 854 #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL 855 #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window 856 #define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) 857 858 // 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. 859 static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in 860 static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear 861 862 // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend) 863 static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow(). 864 static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. 865 static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. 866 867 //------------------------------------------------------------------------- 868 // [SECTION] FORWARD DECLARATIONS 869 //------------------------------------------------------------------------- 870 871 static void SetCurrentWindow(ImGuiWindow* window); 872 static void FindHoveredWindow(); 873 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); 874 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); 875 876 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list); 877 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window); 878 879 // Settings 880 static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); 881 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); 882 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); 883 static void WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); 884 static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf); 885 886 // Platform Dependents default implementation for IO functions 887 static const char* GetClipboardTextFn_DefaultImpl(void* user_data); 888 static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); 889 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); 890 891 namespace ImGui 892 { 893 // Navigation 894 static void NavUpdate(); 895 static void NavUpdateWindowing(); 896 static void NavUpdateWindowingOverlay(); 897 static void NavUpdateMoveResult(); 898 static void NavUpdateInitResult(); 899 static float NavUpdatePageUpPageDown(); 900 static inline void NavUpdateAnyRequestFlag(); 901 static void NavEndFrame(); 902 static bool NavScoreItem(ImGuiNavItemData* result, ImRect cand); 903 static void NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); 904 static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); 905 static ImVec2 NavCalcPreferredRefPos(); 906 static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); 907 static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); 908 static void NavRestoreLayer(ImGuiNavLayer layer); 909 static int FindWindowFocusIndex(ImGuiWindow* window); 910 911 // Error Checking 912 static void ErrorCheckNewFrameSanityChecks(); 913 static void ErrorCheckEndFrameSanityChecks(); 914 915 // Misc 916 static void UpdateSettings(); 917 static void UpdateMouseInputs(); 918 static void UpdateMouseWheel(); 919 static void UpdateTabFocus(); 920 static void UpdateDebugToolItemPicker(); 921 static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); 922 static void RenderWindowOuterBorders(ImGuiWindow* window); 923 static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); 924 static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); 925 926 // Viewports 927 static void UpdateViewportsNewFrame(); 928 929 } 930 931 //----------------------------------------------------------------------------- 932 // [SECTION] CONTEXT AND MEMORY ALLOCATORS 933 //----------------------------------------------------------------------------- 934 935 // DLL users: 936 // - Heaps and globals are not shared across DLL boundaries! 937 // - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from. 938 // - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL). 939 // - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 940 // - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in). 941 942 // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. 943 // - ImGui::CreateContext() will automatically set this pointer if it is NULL. 944 // Change to a different context by calling ImGui::SetCurrentContext(). 945 // - Important: Dear ImGui functions are not thread-safe because of this pointer. 946 // If you want thread-safety to allow N threads to access N different contexts: 947 // - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h: 948 // struct ImGuiContext; 949 // extern thread_local ImGuiContext* MyImGuiTLS; 950 // #define GImGui MyImGuiTLS 951 // And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. 952 // - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 953 // - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace. 954 // - DLL users: read comments above. 955 #ifndef GImGui 956 ImGuiContext* GImGui = NULL; 957 #endif 958 959 // Memory Allocator functions. Use SetAllocatorFunctions() to change them. 960 // - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. 961 // - DLL users: read comments above. 962 #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS 963 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } 964 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } 965 #else 966 static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } 967 static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } 968 #endif 969 static ImGuiMemAllocFunc GImAllocatorAllocFunc = MallocWrapper; 970 static ImGuiMemFreeFunc GImAllocatorFreeFunc = FreeWrapper; 971 static void* GImAllocatorUserData = NULL; 972 973 //----------------------------------------------------------------------------- 974 // [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) 975 //----------------------------------------------------------------------------- 976 977 ImGuiStyle::ImGuiStyle() 978 { 979 Alpha = 1.0f; // Global alpha applies to everything in ImGui 980 WindowPadding = ImVec2(8,8); // Padding within a window 981 WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. 982 WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. 983 WindowMinSize = ImVec2(32,32); // Minimum window size 984 WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text 985 WindowMenuButtonPosition= ImGuiDir_Left; // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left. 986 ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows 987 ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. 988 PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows 989 PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. 990 FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) 991 FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). 992 FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. 993 ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines 994 ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) 995 CellPadding = ImVec2(4,2); // Padding within a table cell 996 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! 997 IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). 998 ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). 999 ScrollbarSize = 14.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar 1000 ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar 1001 GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar 1002 GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. 1003 LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. 1004 TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. 1005 TabBorderSize = 0.0f; // Thickness of border around tabs. 1006 TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. 1007 ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. 1008 ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. 1009 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. 1010 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. 1011 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. 1012 MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. 1013 AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. 1014 AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering. 1015 AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). 1016 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. 1017 CircleTessellationMaxError = 0.30f; // 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. 1018 1019 // Default theme 1020 ImGui::StyleColorsDark(this); 1021 } 1022 1023 // 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. 1024 // 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. 1025 void ImGuiStyle::ScaleAllSizes(float scale_factor) 1026 { 1027 WindowPadding = ImFloor(WindowPadding * scale_factor); 1028 WindowRounding = ImFloor(WindowRounding * scale_factor); 1029 WindowMinSize = ImFloor(WindowMinSize * scale_factor); 1030 ChildRounding = ImFloor(ChildRounding * scale_factor); 1031 PopupRounding = ImFloor(PopupRounding * scale_factor); 1032 FramePadding = ImFloor(FramePadding * scale_factor); 1033 FrameRounding = ImFloor(FrameRounding * scale_factor); 1034 ItemSpacing = ImFloor(ItemSpacing * scale_factor); 1035 ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); 1036 CellPadding = ImFloor(CellPadding * scale_factor); 1037 TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); 1038 IndentSpacing = ImFloor(IndentSpacing * scale_factor); 1039 ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); 1040 ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); 1041 ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); 1042 GrabMinSize = ImFloor(GrabMinSize * scale_factor); 1043 GrabRounding = ImFloor(GrabRounding * scale_factor); 1044 LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor); 1045 TabRounding = ImFloor(TabRounding * scale_factor); 1046 TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; 1047 DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); 1048 DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); 1049 MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); 1050 } 1051 1052 ImGuiIO::ImGuiIO() 1053 { 1054 // Most fields are initialized with zero 1055 memset(this, 0, sizeof(*this)); 1056 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. 1057 1058 // Settings 1059 ConfigFlags = ImGuiConfigFlags_None; 1060 BackendFlags = ImGuiBackendFlags_None; 1061 DisplaySize = ImVec2(-1.0f, -1.0f); 1062 DeltaTime = 1.0f / 60.0f; 1063 IniSavingRate = 5.0f; 1064 IniFilename = "imgui.ini"; 1065 LogFilename = "imgui_log.txt"; 1066 MouseDoubleClickTime = 0.30f; 1067 MouseDoubleClickMaxDist = 6.0f; 1068 for (int i = 0; i < ImGuiKey_COUNT; i++) 1069 KeyMap[i] = -1; 1070 KeyRepeatDelay = 0.275f; 1071 KeyRepeatRate = 0.050f; 1072 UserData = NULL; 1073 1074 Fonts = NULL; 1075 FontGlobalScale = 1.0f; 1076 FontDefault = NULL; 1077 FontAllowUserScaling = false; 1078 DisplayFramebufferScale = ImVec2(1.0f, 1.0f); 1079 1080 // Miscellaneous options 1081 MouseDrawCursor = false; 1082 #ifdef __APPLE__ 1083 ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag 1084 #else 1085 ConfigMacOSXBehaviors = false; 1086 #endif 1087 ConfigInputTextCursorBlink = true; 1088 ConfigWindowsResizeFromEdges = true; 1089 ConfigWindowsMoveFromTitleBarOnly = false; 1090 ConfigMemoryCompactTimer = 60.0f; 1091 1092 // Platform Functions 1093 BackendPlatformName = BackendRendererName = NULL; 1094 BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; 1095 GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations 1096 SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; 1097 ClipboardUserData = NULL; 1098 ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; 1099 ImeWindowHandle = NULL; 1100 1101 // Input (NB: we already have memset zero the entire structure!) 1102 MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 1103 MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); 1104 MouseDragThreshold = 6.0f; 1105 for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; 1106 for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; 1107 for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; 1108 } 1109 1110 // Pass in translated ASCII characters for text input. 1111 // - with glfw you can get those from the callback set in glfwSetCharCallback() 1112 // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message 1113 void ImGuiIO::AddInputCharacter(unsigned int c) 1114 { 1115 if (c != 0) 1116 InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); 1117 } 1118 1119 // UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so 1120 // we should save the high surrogate. 1121 void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c) 1122 { 1123 if (c == 0 && InputQueueSurrogate == 0) 1124 return; 1125 1126 if ((c & 0xFC00) == 0xD800) // High surrogate, must save 1127 { 1128 if (InputQueueSurrogate != 0) 1129 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); 1130 InputQueueSurrogate = c; 1131 return; 1132 } 1133 1134 ImWchar cp = c; 1135 if (InputQueueSurrogate != 0) 1136 { 1137 if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate 1138 { 1139 InputQueueCharacters.push_back(IM_UNICODE_CODEPOINT_INVALID); 1140 } 1141 else 1142 { 1143 #if IM_UNICODE_CODEPOINT_MAX == 0xFFFF 1144 cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar 1145 #else 1146 cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000); 1147 #endif 1148 } 1149 1150 InputQueueSurrogate = 0; 1151 } 1152 InputQueueCharacters.push_back(cp); 1153 } 1154 1155 void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) 1156 { 1157 while (*utf8_chars != 0) 1158 { 1159 unsigned int c = 0; 1160 utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); 1161 if (c != 0) 1162 InputQueueCharacters.push_back((ImWchar)c); 1163 } 1164 } 1165 1166 void ImGuiIO::ClearInputCharacters() 1167 { 1168 InputQueueCharacters.resize(0); 1169 } 1170 1171 //----------------------------------------------------------------------------- 1172 // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) 1173 //----------------------------------------------------------------------------- 1174 1175 ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments) 1176 { 1177 IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau() 1178 ImVec2 p_last = p1; 1179 ImVec2 p_closest; 1180 float p_closest_dist2 = FLT_MAX; 1181 float t_step = 1.0f / (float)num_segments; 1182 for (int i_step = 1; i_step <= num_segments; i_step++) 1183 { 1184 ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step); 1185 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); 1186 float dist2 = ImLengthSqr(p - p_line); 1187 if (dist2 < p_closest_dist2) 1188 { 1189 p_closest = p_line; 1190 p_closest_dist2 = dist2; 1191 } 1192 p_last = p_current; 1193 } 1194 return p_closest; 1195 } 1196 1197 // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp 1198 static void ImBezierCubicClosestPointCasteljauStep(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) 1199 { 1200 float dx = x4 - x1; 1201 float dy = y4 - y1; 1202 float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); 1203 float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); 1204 d2 = (d2 >= 0) ? d2 : -d2; 1205 d3 = (d3 >= 0) ? d3 : -d3; 1206 if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) 1207 { 1208 ImVec2 p_current(x4, y4); 1209 ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); 1210 float dist2 = ImLengthSqr(p - p_line); 1211 if (dist2 < p_closest_dist2) 1212 { 1213 p_closest = p_line; 1214 p_closest_dist2 = dist2; 1215 } 1216 p_last = p_current; 1217 } 1218 else if (level < 10) 1219 { 1220 float x12 = (x1 + x2)*0.5f, y12 = (y1 + y2)*0.5f; 1221 float x23 = (x2 + x3)*0.5f, y23 = (y2 + y3)*0.5f; 1222 float x34 = (x3 + x4)*0.5f, y34 = (y3 + y4)*0.5f; 1223 float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; 1224 float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; 1225 float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; 1226 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); 1227 ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); 1228 } 1229 } 1230 1231 // tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol 1232 // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. 1233 ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) 1234 { 1235 IM_ASSERT(tess_tol > 0.0f); 1236 ImVec2 p_last = p1; 1237 ImVec2 p_closest; 1238 float p_closest_dist2 = FLT_MAX; 1239 ImBezierCubicClosestPointCasteljauStep(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); 1240 return p_closest; 1241 } 1242 1243 ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) 1244 { 1245 ImVec2 ap = p - a; 1246 ImVec2 ab_dir = b - a; 1247 float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; 1248 if (dot < 0.0f) 1249 return a; 1250 float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; 1251 if (dot > ab_len_sqr) 1252 return b; 1253 return a + ab_dir * dot / ab_len_sqr; 1254 } 1255 1256 bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) 1257 { 1258 bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; 1259 bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; 1260 bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; 1261 return ((b1 == b2) && (b2 == b3)); 1262 } 1263 1264 void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) 1265 { 1266 ImVec2 v0 = b - a; 1267 ImVec2 v1 = c - a; 1268 ImVec2 v2 = p - a; 1269 const float denom = v0.x * v1.y - v1.x * v0.y; 1270 out_v = (v2.x * v1.y - v1.x * v2.y) / denom; 1271 out_w = (v0.x * v2.y - v2.x * v0.y) / denom; 1272 out_u = 1.0f - out_v - out_w; 1273 } 1274 1275 ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) 1276 { 1277 ImVec2 proj_ab = ImLineClosestPoint(a, b, p); 1278 ImVec2 proj_bc = ImLineClosestPoint(b, c, p); 1279 ImVec2 proj_ca = ImLineClosestPoint(c, a, p); 1280 float dist2_ab = ImLengthSqr(p - proj_ab); 1281 float dist2_bc = ImLengthSqr(p - proj_bc); 1282 float dist2_ca = ImLengthSqr(p - proj_ca); 1283 float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); 1284 if (m == dist2_ab) 1285 return proj_ab; 1286 if (m == dist2_bc) 1287 return proj_bc; 1288 return proj_ca; 1289 } 1290 1291 //----------------------------------------------------------------------------- 1292 // [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions) 1293 //----------------------------------------------------------------------------- 1294 1295 // Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more. 1296 int ImStricmp(const char* str1, const char* str2) 1297 { 1298 int d; 1299 while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } 1300 return d; 1301 } 1302 1303 int ImStrnicmp(const char* str1, const char* str2, size_t count) 1304 { 1305 int d = 0; 1306 while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } 1307 return d; 1308 } 1309 1310 void ImStrncpy(char* dst, const char* src, size_t count) 1311 { 1312 if (count < 1) 1313 return; 1314 if (count > 1) 1315 strncpy(dst, src, count - 1); 1316 dst[count - 1] = 0; 1317 } 1318 1319 char* ImStrdup(const char* str) 1320 { 1321 size_t len = strlen(str); 1322 void* buf = IM_ALLOC(len + 1); 1323 return (char*)memcpy(buf, (const void*)str, len + 1); 1324 } 1325 1326 char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) 1327 { 1328 size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; 1329 size_t src_size = strlen(src) + 1; 1330 if (dst_buf_size < src_size) 1331 { 1332 IM_FREE(dst); 1333 dst = (char*)IM_ALLOC(src_size); 1334 if (p_dst_size) 1335 *p_dst_size = src_size; 1336 } 1337 return (char*)memcpy(dst, (const void*)src, src_size); 1338 } 1339 1340 const char* ImStrchrRange(const char* str, const char* str_end, char c) 1341 { 1342 const char* p = (const char*)memchr(str, (int)c, str_end - str); 1343 return p; 1344 } 1345 1346 int ImStrlenW(const ImWchar* str) 1347 { 1348 //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bit 1349 int n = 0; 1350 while (*str++) n++; 1351 return n; 1352 } 1353 1354 // Find end-of-line. Return pointer will point to either first \n, either str_end. 1355 const char* ImStreolRange(const char* str, const char* str_end) 1356 { 1357 const char* p = (const char*)memchr(str, '\n', str_end - str); 1358 return p ? p : str_end; 1359 } 1360 1361 const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line 1362 { 1363 while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') 1364 buf_mid_line--; 1365 return buf_mid_line; 1366 } 1367 1368 const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) 1369 { 1370 if (!needle_end) 1371 needle_end = needle + strlen(needle); 1372 1373 const char un0 = (char)toupper(*needle); 1374 while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) 1375 { 1376 if (toupper(*haystack) == un0) 1377 { 1378 const char* b = needle + 1; 1379 for (const char* a = haystack + 1; b < needle_end; a++, b++) 1380 if (toupper(*a) != toupper(*b)) 1381 break; 1382 if (b == needle_end) 1383 return haystack; 1384 } 1385 haystack++; 1386 } 1387 return NULL; 1388 } 1389 1390 // 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. 1391 void ImStrTrimBlanks(char* buf) 1392 { 1393 char* p = buf; 1394 while (p[0] == ' ' || p[0] == '\t') // Leading blanks 1395 p++; 1396 char* p_start = p; 1397 while (*p != 0) // Find end of string 1398 p++; 1399 while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks 1400 p--; 1401 if (p_start != buf) // Copy memory if we had leading blanks 1402 memmove(buf, p_start, p - p_start); 1403 buf[p - p_start] = 0; // Zero terminate 1404 } 1405 1406 const char* ImStrSkipBlank(const char* str) 1407 { 1408 while (str[0] == ' ' || str[0] == '\t') 1409 str++; 1410 return str; 1411 } 1412 1413 // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). 1414 // 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. 1415 // B) When buf==NULL vsnprintf() will return the output size. 1416 #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1417 1418 // We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h) 1419 // You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1420 // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are 1421 // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) 1422 #ifdef IMGUI_USE_STB_SPRINTF 1423 #define STB_SPRINTF_IMPLEMENTATION 1424 #include "stb_sprintf.h" 1425 #endif 1426 1427 #if defined(_MSC_VER) && !defined(vsnprintf) 1428 #define vsnprintf _vsnprintf 1429 #endif 1430 1431 int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) 1432 { 1433 va_list args; 1434 va_start(args, fmt); 1435 #ifdef IMGUI_USE_STB_SPRINTF 1436 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); 1437 #else 1438 int w = vsnprintf(buf, buf_size, fmt, args); 1439 #endif 1440 va_end(args); 1441 if (buf == NULL) 1442 return w; 1443 if (w == -1 || w >= (int)buf_size) 1444 w = (int)buf_size - 1; 1445 buf[w] = 0; 1446 return w; 1447 } 1448 1449 int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) 1450 { 1451 #ifdef IMGUI_USE_STB_SPRINTF 1452 int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); 1453 #else 1454 int w = vsnprintf(buf, buf_size, fmt, args); 1455 #endif 1456 if (buf == NULL) 1457 return w; 1458 if (w == -1 || w >= (int)buf_size) 1459 w = (int)buf_size - 1; 1460 buf[w] = 0; 1461 return w; 1462 } 1463 #endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS 1464 1465 // CRC32 needs a 1KB lookup table (not cache friendly) 1466 // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: 1467 // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. 1468 static const ImU32 GCrc32LookupTable[256] = 1469 { 1470 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, 1471 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, 1472 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, 1473 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, 1474 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, 1475 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, 1476 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, 1477 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, 1478 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, 1479 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, 1480 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, 1481 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, 1482 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, 1483 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, 1484 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, 1485 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, 1486 }; 1487 1488 // Known size hash 1489 // It is ok to call ImHashData on a string with known length but the ### operator won't be supported. 1490 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. 1491 ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed) 1492 { 1493 ImU32 crc = ~seed; 1494 const unsigned char* data = (const unsigned char*)data_p; 1495 const ImU32* crc32_lut = GCrc32LookupTable; 1496 while (data_size-- != 0) 1497 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; 1498 return ~crc; 1499 } 1500 1501 // Zero-terminated string hash, with support for ### to reset back to seed value 1502 // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. 1503 // Because this syntax is rarely used we are optimizing for the common case. 1504 // - If we reach ### in the string we discard the hash so far and reset to the seed. 1505 // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) 1506 // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. 1507 ImGuiID ImHashStr(const char* data_p, size_t data_size, ImU32 seed) 1508 { 1509 seed = ~seed; 1510 ImU32 crc = seed; 1511 const unsigned char* data = (const unsigned char*)data_p; 1512 const ImU32* crc32_lut = GCrc32LookupTable; 1513 if (data_size != 0) 1514 { 1515 while (data_size-- != 0) 1516 { 1517 unsigned char c = *data++; 1518 if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') 1519 crc = seed; 1520 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1521 } 1522 } 1523 else 1524 { 1525 while (unsigned char c = *data++) 1526 { 1527 if (c == '#' && data[0] == '#' && data[1] == '#') 1528 crc = seed; 1529 crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; 1530 } 1531 } 1532 return ~crc; 1533 } 1534 1535 //----------------------------------------------------------------------------- 1536 // [SECTION] MISC HELPERS/UTILITIES (File functions) 1537 //----------------------------------------------------------------------------- 1538 1539 // Default file functions 1540 #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 1541 1542 ImFileHandle ImFileOpen(const char* filename, const char* mode) 1543 { 1544 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__) 1545 // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. 1546 // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! 1547 const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); 1548 const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); 1549 ImVector<ImWchar> buf; 1550 buf.resize(filename_wsize + mode_wsize); 1551 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize); 1552 ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize); 1553 return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]); 1554 #else 1555 return fopen(filename, mode); 1556 #endif 1557 } 1558 1559 // 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. 1560 bool ImFileClose(ImFileHandle f) { return fclose(f) == 0; } 1561 ImU64 ImFileGetSize(ImFileHandle f) { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; } 1562 ImU64 ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fread(data, (size_t)sz, (size_t)count, f); } 1563 ImU64 ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f) { return fwrite(data, (size_t)sz, (size_t)count, f); } 1564 #endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS 1565 1566 // Helper: Load file content into memory 1567 // Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree() 1568 // This can't really be used with "rt" because fseek size won't match read size. 1569 void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes) 1570 { 1571 IM_ASSERT(filename && mode); 1572 if (out_file_size) 1573 *out_file_size = 0; 1574 1575 ImFileHandle f; 1576 if ((f = ImFileOpen(filename, mode)) == NULL) 1577 return NULL; 1578 1579 size_t file_size = (size_t)ImFileGetSize(f); 1580 if (file_size == (size_t)-1) 1581 { 1582 ImFileClose(f); 1583 return NULL; 1584 } 1585 1586 void* file_data = IM_ALLOC(file_size + padding_bytes); 1587 if (file_data == NULL) 1588 { 1589 ImFileClose(f); 1590 return NULL; 1591 } 1592 if (ImFileRead(file_data, 1, file_size, f) != file_size) 1593 { 1594 ImFileClose(f); 1595 IM_FREE(file_data); 1596 return NULL; 1597 } 1598 if (padding_bytes > 0) 1599 memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); 1600 1601 ImFileClose(f); 1602 if (out_file_size) 1603 *out_file_size = file_size; 1604 1605 return file_data; 1606 } 1607 1608 //----------------------------------------------------------------------------- 1609 // [SECTION] MISC HELPERS/UTILITIES (ImText* functions) 1610 //----------------------------------------------------------------------------- 1611 1612 // Convert UTF-8 to 32-bit character, process single character input. 1613 // A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8). 1614 // We handle UTF-8 decoding error by skipping forward. 1615 int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) 1616 { 1617 static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 }; 1618 static const int masks[] = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 }; 1619 static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 }; 1620 static const int shiftc[] = { 0, 18, 12, 6, 0 }; 1621 static const int shifte[] = { 0, 6, 4, 2, 0 }; 1622 int len = lengths[*(const unsigned char*)in_text >> 3]; 1623 int wanted = len + !len; 1624 1625 if (in_text_end == NULL) 1626 in_text_end = in_text + wanted; // Max length, nulls will be taken into account. 1627 1628 // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here, 1629 // so it is fast even with excessive branching. 1630 unsigned char s[4]; 1631 s[0] = in_text + 0 < in_text_end ? in_text[0] : 0; 1632 s[1] = in_text + 1 < in_text_end ? in_text[1] : 0; 1633 s[2] = in_text + 2 < in_text_end ? in_text[2] : 0; 1634 s[3] = in_text + 3 < in_text_end ? in_text[3] : 0; 1635 1636 // Assume a four-byte character and load four bytes. Unused bits are shifted out. 1637 *out_char = (uint32_t)(s[0] & masks[len]) << 18; 1638 *out_char |= (uint32_t)(s[1] & 0x3f) << 12; 1639 *out_char |= (uint32_t)(s[2] & 0x3f) << 6; 1640 *out_char |= (uint32_t)(s[3] & 0x3f) << 0; 1641 *out_char >>= shiftc[len]; 1642 1643 // Accumulate the various error conditions. 1644 int e = 0; 1645 e = (*out_char < mins[len]) << 6; // non-canonical encoding 1646 e |= ((*out_char >> 11) == 0x1b) << 7; // surrogate half? 1647 e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8; // out of range? 1648 e |= (s[1] & 0xc0) >> 2; 1649 e |= (s[2] & 0xc0) >> 4; 1650 e |= (s[3] ) >> 6; 1651 e ^= 0x2a; // top two bits of each tail byte correct? 1652 e >>= shifte[len]; 1653 1654 if (e) 1655 { 1656 // No bytes are consumed when *in_text == 0 || in_text == in_text_end. 1657 // One byte is consumed in case of invalid first byte of in_text. 1658 // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes. 1659 // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s. 1660 wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]); 1661 *out_char = IM_UNICODE_CODEPOINT_INVALID; 1662 } 1663 1664 return wanted; 1665 } 1666 1667 int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) 1668 { 1669 ImWchar* buf_out = buf; 1670 ImWchar* buf_end = buf + buf_size; 1671 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) 1672 { 1673 unsigned int c; 1674 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1675 if (c == 0) 1676 break; 1677 *buf_out++ = (ImWchar)c; 1678 } 1679 *buf_out = 0; 1680 if (in_text_remaining) 1681 *in_text_remaining = in_text; 1682 return (int)(buf_out - buf); 1683 } 1684 1685 int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) 1686 { 1687 int char_count = 0; 1688 while ((!in_text_end || in_text < in_text_end) && *in_text) 1689 { 1690 unsigned int c; 1691 in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); 1692 if (c == 0) 1693 break; 1694 char_count++; 1695 } 1696 return char_count; 1697 } 1698 1699 // Based on stb_to_utf8() from github.com/nothings/stb/ 1700 static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) 1701 { 1702 if (c < 0x80) 1703 { 1704 buf[0] = (char)c; 1705 return 1; 1706 } 1707 if (c < 0x800) 1708 { 1709 if (buf_size < 2) return 0; 1710 buf[0] = (char)(0xc0 + (c >> 6)); 1711 buf[1] = (char)(0x80 + (c & 0x3f)); 1712 return 2; 1713 } 1714 if (c < 0x10000) 1715 { 1716 if (buf_size < 3) return 0; 1717 buf[0] = (char)(0xe0 + (c >> 12)); 1718 buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); 1719 buf[2] = (char)(0x80 + ((c ) & 0x3f)); 1720 return 3; 1721 } 1722 if (c <= 0x10FFFF) 1723 { 1724 if (buf_size < 4) return 0; 1725 buf[0] = (char)(0xf0 + (c >> 18)); 1726 buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); 1727 buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); 1728 buf[3] = (char)(0x80 + ((c ) & 0x3f)); 1729 return 4; 1730 } 1731 // Invalid code point, the max unicode is 0x10FFFF 1732 return 0; 1733 } 1734 1735 // Not optimal but we very rarely use this function. 1736 int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) 1737 { 1738 unsigned int unused = 0; 1739 return ImTextCharFromUtf8(&unused, in_text, in_text_end); 1740 } 1741 1742 static inline int ImTextCountUtf8BytesFromChar(unsigned int c) 1743 { 1744 if (c < 0x80) return 1; 1745 if (c < 0x800) return 2; 1746 if (c < 0x10000) return 3; 1747 if (c <= 0x10FFFF) return 4; 1748 return 3; 1749 } 1750 1751 int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) 1752 { 1753 char* buf_out = buf; 1754 const char* buf_end = buf + buf_size; 1755 while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) 1756 { 1757 unsigned int c = (unsigned int)(*in_text++); 1758 if (c < 0x80) 1759 *buf_out++ = (char)c; 1760 else 1761 buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c); 1762 } 1763 *buf_out = 0; 1764 return (int)(buf_out - buf); 1765 } 1766 1767 int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) 1768 { 1769 int bytes_count = 0; 1770 while ((!in_text_end || in_text < in_text_end) && *in_text) 1771 { 1772 unsigned int c = (unsigned int)(*in_text++); 1773 if (c < 0x80) 1774 bytes_count++; 1775 else 1776 bytes_count += ImTextCountUtf8BytesFromChar(c); 1777 } 1778 return bytes_count; 1779 } 1780 1781 //----------------------------------------------------------------------------- 1782 // [SECTION] MISC HELPERS/UTILITIES (Color functions) 1783 // Note: The Convert functions are early design which are not consistent with other API. 1784 //----------------------------------------------------------------------------- 1785 1786 IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) 1787 { 1788 float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; 1789 int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); 1790 int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); 1791 int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); 1792 return IM_COL32(r, g, b, 0xFF); 1793 } 1794 1795 ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) 1796 { 1797 float s = 1.0f / 255.0f; 1798 return ImVec4( 1799 ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, 1800 ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, 1801 ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, 1802 ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); 1803 } 1804 1805 ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) 1806 { 1807 ImU32 out; 1808 out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; 1809 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; 1810 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; 1811 out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; 1812 return out; 1813 } 1814 1815 // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 1816 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv 1817 void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) 1818 { 1819 float K = 0.f; 1820 if (g < b) 1821 { 1822 ImSwap(g, b); 1823 K = -1.f; 1824 } 1825 if (r < g) 1826 { 1827 ImSwap(r, g); 1828 K = -2.f / 6.f - K; 1829 } 1830 1831 const float chroma = r - (g < b ? g : b); 1832 out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); 1833 out_s = chroma / (r + 1e-20f); 1834 out_v = r; 1835 } 1836 1837 // Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 1838 // also http://en.wikipedia.org/wiki/HSL_and_HSV 1839 void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) 1840 { 1841 if (s == 0.0f) 1842 { 1843 // gray 1844 out_r = out_g = out_b = v; 1845 return; 1846 } 1847 1848 h = ImFmod(h, 1.0f) / (60.0f / 360.0f); 1849 int i = (int)h; 1850 float f = h - (float)i; 1851 float p = v * (1.0f - s); 1852 float q = v * (1.0f - s * f); 1853 float t = v * (1.0f - s * (1.0f - f)); 1854 1855 switch (i) 1856 { 1857 case 0: out_r = v; out_g = t; out_b = p; break; 1858 case 1: out_r = q; out_g = v; out_b = p; break; 1859 case 2: out_r = p; out_g = v; out_b = t; break; 1860 case 3: out_r = p; out_g = q; out_b = v; break; 1861 case 4: out_r = t; out_g = p; out_b = v; break; 1862 case 5: default: out_r = v; out_g = p; out_b = q; break; 1863 } 1864 } 1865 1866 //----------------------------------------------------------------------------- 1867 // [SECTION] ImGuiStorage 1868 // Helper: Key->value storage 1869 //----------------------------------------------------------------------------- 1870 1871 // std::lower_bound but without the bullshit 1872 static ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key) 1873 { 1874 ImGuiStorage::ImGuiStoragePair* first = data.Data; 1875 ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size; 1876 size_t count = (size_t)(last - first); 1877 while (count > 0) 1878 { 1879 size_t count2 = count >> 1; 1880 ImGuiStorage::ImGuiStoragePair* mid = first + count2; 1881 if (mid->key < key) 1882 { 1883 first = ++mid; 1884 count -= count2 + 1; 1885 } 1886 else 1887 { 1888 count = count2; 1889 } 1890 } 1891 return first; 1892 } 1893 1894 // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. 1895 void ImGuiStorage::BuildSortByKey() 1896 { 1897 struct StaticFunc 1898 { 1899 static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) 1900 { 1901 // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. 1902 if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; 1903 if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1; 1904 return 0; 1905 } 1906 }; 1907 if (Data.Size > 1) 1908 ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); 1909 } 1910 1911 int ImGuiStorage::GetInt(ImGuiID key, int default_val) const 1912 { 1913 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1914 if (it == Data.end() || it->key != key) 1915 return default_val; 1916 return it->val_i; 1917 } 1918 1919 bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const 1920 { 1921 return GetInt(key, default_val ? 1 : 0) != 0; 1922 } 1923 1924 float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const 1925 { 1926 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1927 if (it == Data.end() || it->key != key) 1928 return default_val; 1929 return it->val_f; 1930 } 1931 1932 void* ImGuiStorage::GetVoidPtr(ImGuiID key) const 1933 { 1934 ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key); 1935 if (it == Data.end() || it->key != key) 1936 return NULL; 1937 return it->val_p; 1938 } 1939 1940 // 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. 1941 int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) 1942 { 1943 ImGuiStoragePair* it = LowerBound(Data, key); 1944 if (it == Data.end() || it->key != key) 1945 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1946 return &it->val_i; 1947 } 1948 1949 bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) 1950 { 1951 return (bool*)GetIntRef(key, default_val ? 1 : 0); 1952 } 1953 1954 float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) 1955 { 1956 ImGuiStoragePair* it = LowerBound(Data, key); 1957 if (it == Data.end() || it->key != key) 1958 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1959 return &it->val_f; 1960 } 1961 1962 void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) 1963 { 1964 ImGuiStoragePair* it = LowerBound(Data, key); 1965 if (it == Data.end() || it->key != key) 1966 it = Data.insert(it, ImGuiStoragePair(key, default_val)); 1967 return &it->val_p; 1968 } 1969 1970 // 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) 1971 void ImGuiStorage::SetInt(ImGuiID key, int val) 1972 { 1973 ImGuiStoragePair* it = LowerBound(Data, key); 1974 if (it == Data.end() || it->key != key) 1975 { 1976 Data.insert(it, ImGuiStoragePair(key, val)); 1977 return; 1978 } 1979 it->val_i = val; 1980 } 1981 1982 void ImGuiStorage::SetBool(ImGuiID key, bool val) 1983 { 1984 SetInt(key, val ? 1 : 0); 1985 } 1986 1987 void ImGuiStorage::SetFloat(ImGuiID key, float val) 1988 { 1989 ImGuiStoragePair* it = LowerBound(Data, key); 1990 if (it == Data.end() || it->key != key) 1991 { 1992 Data.insert(it, ImGuiStoragePair(key, val)); 1993 return; 1994 } 1995 it->val_f = val; 1996 } 1997 1998 void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) 1999 { 2000 ImGuiStoragePair* it = LowerBound(Data, key); 2001 if (it == Data.end() || it->key != key) 2002 { 2003 Data.insert(it, ImGuiStoragePair(key, val)); 2004 return; 2005 } 2006 it->val_p = val; 2007 } 2008 2009 void ImGuiStorage::SetAllInt(int v) 2010 { 2011 for (int i = 0; i < Data.Size; i++) 2012 Data[i].val_i = v; 2013 } 2014 2015 //----------------------------------------------------------------------------- 2016 // [SECTION] ImGuiTextFilter 2017 //----------------------------------------------------------------------------- 2018 2019 // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" 2020 ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) 2021 { 2022 if (default_filter) 2023 { 2024 ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); 2025 Build(); 2026 } 2027 else 2028 { 2029 InputBuf[0] = 0; 2030 CountGrep = 0; 2031 } 2032 } 2033 2034 bool ImGuiTextFilter::Draw(const char* label, float width) 2035 { 2036 if (width != 0.0f) 2037 ImGui::SetNextItemWidth(width); 2038 bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); 2039 if (value_changed) 2040 Build(); 2041 return value_changed; 2042 } 2043 2044 void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const 2045 { 2046 out->resize(0); 2047 const char* wb = b; 2048 const char* we = wb; 2049 while (we < e) 2050 { 2051 if (*we == separator) 2052 { 2053 out->push_back(ImGuiTextRange(wb, we)); 2054 wb = we + 1; 2055 } 2056 we++; 2057 } 2058 if (wb != we) 2059 out->push_back(ImGuiTextRange(wb, we)); 2060 } 2061 2062 void ImGuiTextFilter::Build() 2063 { 2064 Filters.resize(0); 2065 ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf)); 2066 input_range.split(',', &Filters); 2067 2068 CountGrep = 0; 2069 for (int i = 0; i != Filters.Size; i++) 2070 { 2071 ImGuiTextRange& f = Filters[i]; 2072 while (f.b < f.e && ImCharIsBlankA(f.b[0])) 2073 f.b++; 2074 while (f.e > f.b && ImCharIsBlankA(f.e[-1])) 2075 f.e--; 2076 if (f.empty()) 2077 continue; 2078 if (Filters[i].b[0] != '-') 2079 CountGrep += 1; 2080 } 2081 } 2082 2083 bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const 2084 { 2085 if (Filters.empty()) 2086 return true; 2087 2088 if (text == NULL) 2089 text = ""; 2090 2091 for (int i = 0; i != Filters.Size; i++) 2092 { 2093 const ImGuiTextRange& f = Filters[i]; 2094 if (f.empty()) 2095 continue; 2096 if (f.b[0] == '-') 2097 { 2098 // Subtract 2099 if (ImStristr(text, text_end, f.b + 1, f.e) != NULL) 2100 return false; 2101 } 2102 else 2103 { 2104 // Grep 2105 if (ImStristr(text, text_end, f.b, f.e) != NULL) 2106 return true; 2107 } 2108 } 2109 2110 // Implicit * grep 2111 if (CountGrep == 0) 2112 return true; 2113 2114 return false; 2115 } 2116 2117 //----------------------------------------------------------------------------- 2118 // [SECTION] ImGuiTextBuffer 2119 //----------------------------------------------------------------------------- 2120 2121 // On some platform vsnprintf() takes va_list by reference and modifies it. 2122 // va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. 2123 #ifndef va_copy 2124 #if defined(__GNUC__) || defined(__clang__) 2125 #define va_copy(dest, src) __builtin_va_copy(dest, src) 2126 #else 2127 #define va_copy(dest, src) (dest = src) 2128 #endif 2129 #endif 2130 2131 char ImGuiTextBuffer::EmptyString[1] = { 0 }; 2132 2133 void ImGuiTextBuffer::append(const char* str, const char* str_end) 2134 { 2135 int len = str_end ? (int)(str_end - str) : (int)strlen(str); 2136 2137 // Add zero-terminator the first time 2138 const int write_off = (Buf.Size != 0) ? Buf.Size : 1; 2139 const int needed_sz = write_off + len; 2140 if (write_off + len >= Buf.Capacity) 2141 { 2142 int new_capacity = Buf.Capacity * 2; 2143 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); 2144 } 2145 2146 Buf.resize(needed_sz); 2147 memcpy(&Buf[write_off - 1], str, (size_t)len); 2148 Buf[write_off - 1 + len] = 0; 2149 } 2150 2151 void ImGuiTextBuffer::appendf(const char* fmt, ...) 2152 { 2153 va_list args; 2154 va_start(args, fmt); 2155 appendfv(fmt, args); 2156 va_end(args); 2157 } 2158 2159 // Helper: Text buffer for logging/accumulating text 2160 void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) 2161 { 2162 va_list args_copy; 2163 va_copy(args_copy, args); 2164 2165 int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. 2166 if (len <= 0) 2167 { 2168 va_end(args_copy); 2169 return; 2170 } 2171 2172 // Add zero-terminator the first time 2173 const int write_off = (Buf.Size != 0) ? Buf.Size : 1; 2174 const int needed_sz = write_off + len; 2175 if (write_off + len >= Buf.Capacity) 2176 { 2177 int new_capacity = Buf.Capacity * 2; 2178 Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); 2179 } 2180 2181 Buf.resize(needed_sz); 2182 ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); 2183 va_end(args_copy); 2184 } 2185 2186 //----------------------------------------------------------------------------- 2187 // [SECTION] ImGuiListClipper 2188 // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed 2189 // the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO) 2190 //----------------------------------------------------------------------------- 2191 2192 // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell. 2193 // The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous. 2194 static bool GetSkipItemForListClipping() 2195 { 2196 ImGuiContext& g = *GImGui; 2197 return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); 2198 } 2199 2200 // Helper to calculate coarse clipping of large list of evenly sized items. 2201 // NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. 2202 // NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX 2203 void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) 2204 { 2205 ImGuiContext& g = *GImGui; 2206 ImGuiWindow* window = g.CurrentWindow; 2207 if (g.LogEnabled) 2208 { 2209 // If logging is active, do not perform any clipping 2210 *out_items_display_start = 0; 2211 *out_items_display_end = items_count; 2212 return; 2213 } 2214 if (GetSkipItemForListClipping()) 2215 { 2216 *out_items_display_start = *out_items_display_end = 0; 2217 return; 2218 } 2219 2220 // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect 2221 ImRect unclipped_rect = window->ClipRect; 2222 if (g.NavMoveRequest) 2223 unclipped_rect.Add(g.NavScoringRect); 2224 if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) 2225 unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); 2226 2227 const ImVec2 pos = window->DC.CursorPos; 2228 int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); 2229 int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); 2230 2231 // When performing a navigation request, ensure we have one item extra in the direction we are moving to 2232 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) 2233 start--; 2234 if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) 2235 end++; 2236 2237 start = ImClamp(start, 0, items_count); 2238 end = ImClamp(end + 1, start, items_count); 2239 *out_items_display_start = start; 2240 *out_items_display_end = end; 2241 } 2242 2243 static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) 2244 { 2245 // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. 2246 // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. 2247 // The clipper should probably have a 4th step to display the last item in a regular manner. 2248 ImGuiContext& g = *GImGui; 2249 ImGuiWindow* window = g.CurrentWindow; 2250 float off_y = pos_y - window->DC.CursorPos.y; 2251 window->DC.CursorPos.y = pos_y; 2252 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); 2253 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. 2254 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. 2255 if (ImGuiOldColumns* columns = window->DC.CurrentColumns) 2256 columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly 2257 if (ImGuiTable* table = g.CurrentTable) 2258 { 2259 if (table->IsInsideRow) 2260 ImGui::TableEndRow(table); 2261 table->RowPosY2 = window->DC.CursorPos.y; 2262 const int row_increase = (int)((off_y / line_height) + 0.5f); 2263 //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow() 2264 table->RowBgColorCounter += row_increase; 2265 } 2266 } 2267 2268 ImGuiListClipper::ImGuiListClipper() 2269 { 2270 memset(this, 0, sizeof(*this)); 2271 ItemsCount = -1; 2272 } 2273 2274 ImGuiListClipper::~ImGuiListClipper() 2275 { 2276 IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); 2277 } 2278 2279 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 2280 // Use case B: Begin() called from constructor with items_height>0 2281 // 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. 2282 void ImGuiListClipper::Begin(int items_count, float items_height) 2283 { 2284 ImGuiContext& g = *GImGui; 2285 ImGuiWindow* window = g.CurrentWindow; 2286 2287 if (ImGuiTable* table = g.CurrentTable) 2288 if (table->IsInsideRow) 2289 ImGui::TableEndRow(table); 2290 2291 StartPosY = window->DC.CursorPos.y; 2292 ItemsHeight = items_height; 2293 ItemsCount = items_count; 2294 ItemsFrozen = 0; 2295 StepNo = 0; 2296 DisplayStart = -1; 2297 DisplayEnd = 0; 2298 } 2299 2300 void ImGuiListClipper::End() 2301 { 2302 if (ItemsCount < 0) // Already ended 2303 return; 2304 2305 // 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. 2306 if (ItemsCount < INT_MAX && DisplayStart >= 0) 2307 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); 2308 ItemsCount = -1; 2309 StepNo = 3; 2310 } 2311 2312 bool ImGuiListClipper::Step() 2313 { 2314 ImGuiContext& g = *GImGui; 2315 ImGuiWindow* window = g.CurrentWindow; 2316 2317 ImGuiTable* table = g.CurrentTable; 2318 if (table && table->IsInsideRow) 2319 ImGui::TableEndRow(table); 2320 2321 // No items 2322 if (ItemsCount == 0 || GetSkipItemForListClipping()) 2323 { 2324 End(); 2325 return false; 2326 } 2327 2328 // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) 2329 if (StepNo == 0) 2330 { 2331 // While we are in frozen row state, keep displaying items one by one, unclipped 2332 // FIXME: Could be stored as a table-agnostic state. 2333 if (table != NULL && !table->IsUnfrozenRows) 2334 { 2335 DisplayStart = ItemsFrozen; 2336 DisplayEnd = ItemsFrozen + 1; 2337 ItemsFrozen++; 2338 return true; 2339 } 2340 2341 StartPosY = window->DC.CursorPos.y; 2342 if (ItemsHeight <= 0.0f) 2343 { 2344 // Submit the first item so we can measure its height (generally it is 0..1) 2345 DisplayStart = ItemsFrozen; 2346 DisplayEnd = ItemsFrozen + 1; 2347 StepNo = 1; 2348 return true; 2349 } 2350 2351 // Already has item height (given by user in Begin): skip to calculating step 2352 DisplayStart = DisplayEnd; 2353 StepNo = 2; 2354 } 2355 2356 // Step 1: the clipper infer height from first element 2357 if (StepNo == 1) 2358 { 2359 IM_ASSERT(ItemsHeight <= 0.0f); 2360 if (table) 2361 { 2362 const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row 2363 const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell. 2364 ItemsHeight = pos_y2 - pos_y1; 2365 window->DC.CursorPos.y = pos_y2; 2366 } 2367 else 2368 { 2369 ItemsHeight = window->DC.CursorPos.y - StartPosY; 2370 } 2371 IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); 2372 StepNo = 2; 2373 } 2374 2375 // Reached end of list 2376 if (DisplayEnd >= ItemsCount) 2377 { 2378 End(); 2379 return false; 2380 } 2381 2382 // Step 2: calculate the actual range of elements to display, and position the cursor before the first element 2383 if (StepNo == 2) 2384 { 2385 IM_ASSERT(ItemsHeight > 0.0f); 2386 2387 int already_submitted = DisplayEnd; 2388 ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); 2389 DisplayStart += already_submitted; 2390 DisplayEnd += already_submitted; 2391 2392 // Seek cursor 2393 if (DisplayStart > already_submitted) 2394 SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight); 2395 2396 StepNo = 3; 2397 return true; 2398 } 2399 2400 // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), 2401 // Advance the cursor to the end of the list and then returns 'false' to end the loop. 2402 if (StepNo == 3) 2403 { 2404 // Seek cursor 2405 if (ItemsCount < INT_MAX) 2406 SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor 2407 ItemsCount = -1; 2408 return false; 2409 } 2410 2411 IM_ASSERT(0); 2412 return false; 2413 } 2414 2415 //----------------------------------------------------------------------------- 2416 // [SECTION] STYLING 2417 //----------------------------------------------------------------------------- 2418 2419 ImGuiStyle& ImGui::GetStyle() 2420 { 2421 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 2422 return GImGui->Style; 2423 } 2424 2425 ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) 2426 { 2427 ImGuiStyle& style = GImGui->Style; 2428 ImVec4 c = style.Colors[idx]; 2429 c.w *= style.Alpha * alpha_mul; 2430 return ColorConvertFloat4ToU32(c); 2431 } 2432 2433 ImU32 ImGui::GetColorU32(const ImVec4& col) 2434 { 2435 ImGuiStyle& style = GImGui->Style; 2436 ImVec4 c = col; 2437 c.w *= style.Alpha; 2438 return ColorConvertFloat4ToU32(c); 2439 } 2440 2441 const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) 2442 { 2443 ImGuiStyle& style = GImGui->Style; 2444 return style.Colors[idx]; 2445 } 2446 2447 ImU32 ImGui::GetColorU32(ImU32 col) 2448 { 2449 ImGuiStyle& style = GImGui->Style; 2450 if (style.Alpha >= 1.0f) 2451 return col; 2452 ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; 2453 a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. 2454 return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); 2455 } 2456 2457 // 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 2458 void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) 2459 { 2460 ImGuiContext& g = *GImGui; 2461 ImGuiColorMod backup; 2462 backup.Col = idx; 2463 backup.BackupValue = g.Style.Colors[idx]; 2464 g.ColorStack.push_back(backup); 2465 g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); 2466 } 2467 2468 void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) 2469 { 2470 ImGuiContext& g = *GImGui; 2471 ImGuiColorMod backup; 2472 backup.Col = idx; 2473 backup.BackupValue = g.Style.Colors[idx]; 2474 g.ColorStack.push_back(backup); 2475 g.Style.Colors[idx] = col; 2476 } 2477 2478 void ImGui::PopStyleColor(int count) 2479 { 2480 ImGuiContext& g = *GImGui; 2481 while (count > 0) 2482 { 2483 ImGuiColorMod& backup = g.ColorStack.back(); 2484 g.Style.Colors[backup.Col] = backup.BackupValue; 2485 g.ColorStack.pop_back(); 2486 count--; 2487 } 2488 } 2489 2490 struct ImGuiStyleVarInfo 2491 { 2492 ImGuiDataType Type; 2493 ImU32 Count; 2494 ImU32 Offset; 2495 void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } 2496 }; 2497 2498 static const ImGuiStyleVarInfo GStyleVarInfo[] = 2499 { 2500 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha 2501 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding 2502 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding 2503 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize 2504 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize 2505 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign 2506 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding 2507 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize 2508 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding 2509 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize 2510 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding 2511 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding 2512 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize 2513 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing 2514 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing 2515 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing 2516 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding 2517 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize 2518 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding 2519 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize 2520 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding 2521 { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding 2522 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign 2523 { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign 2524 }; 2525 2526 static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) 2527 { 2528 IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); 2529 IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT); 2530 return &GStyleVarInfo[idx]; 2531 } 2532 2533 void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) 2534 { 2535 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 2536 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) 2537 { 2538 ImGuiContext& g = *GImGui; 2539 float* pvar = (float*)var_info->GetVarPtr(&g.Style); 2540 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); 2541 *pvar = val; 2542 return; 2543 } 2544 IM_ASSERT(0 && "Called PushStyleVar() float variant but variable is not a float!"); 2545 } 2546 2547 void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) 2548 { 2549 const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); 2550 if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) 2551 { 2552 ImGuiContext& g = *GImGui; 2553 ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); 2554 g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar)); 2555 *pvar = val; 2556 return; 2557 } 2558 IM_ASSERT(0 && "Called PushStyleVar() ImVec2 variant but variable is not a ImVec2!"); 2559 } 2560 2561 void ImGui::PopStyleVar(int count) 2562 { 2563 ImGuiContext& g = *GImGui; 2564 while (count > 0) 2565 { 2566 // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. 2567 ImGuiStyleMod& backup = g.StyleVarStack.back(); 2568 const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); 2569 void* data = info->GetVarPtr(&g.Style); 2570 if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } 2571 else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } 2572 g.StyleVarStack.pop_back(); 2573 count--; 2574 } 2575 } 2576 2577 const char* ImGui::GetStyleColorName(ImGuiCol idx) 2578 { 2579 // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; 2580 switch (idx) 2581 { 2582 case ImGuiCol_Text: return "Text"; 2583 case ImGuiCol_TextDisabled: return "TextDisabled"; 2584 case ImGuiCol_WindowBg: return "WindowBg"; 2585 case ImGuiCol_ChildBg: return "ChildBg"; 2586 case ImGuiCol_PopupBg: return "PopupBg"; 2587 case ImGuiCol_Border: return "Border"; 2588 case ImGuiCol_BorderShadow: return "BorderShadow"; 2589 case ImGuiCol_FrameBg: return "FrameBg"; 2590 case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; 2591 case ImGuiCol_FrameBgActive: return "FrameBgActive"; 2592 case ImGuiCol_TitleBg: return "TitleBg"; 2593 case ImGuiCol_TitleBgActive: return "TitleBgActive"; 2594 case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; 2595 case ImGuiCol_MenuBarBg: return "MenuBarBg"; 2596 case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; 2597 case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; 2598 case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; 2599 case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; 2600 case ImGuiCol_CheckMark: return "CheckMark"; 2601 case ImGuiCol_SliderGrab: return "SliderGrab"; 2602 case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; 2603 case ImGuiCol_Button: return "Button"; 2604 case ImGuiCol_ButtonHovered: return "ButtonHovered"; 2605 case ImGuiCol_ButtonActive: return "ButtonActive"; 2606 case ImGuiCol_Header: return "Header"; 2607 case ImGuiCol_HeaderHovered: return "HeaderHovered"; 2608 case ImGuiCol_HeaderActive: return "HeaderActive"; 2609 case ImGuiCol_Separator: return "Separator"; 2610 case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; 2611 case ImGuiCol_SeparatorActive: return "SeparatorActive"; 2612 case ImGuiCol_ResizeGrip: return "ResizeGrip"; 2613 case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; 2614 case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; 2615 case ImGuiCol_Tab: return "Tab"; 2616 case ImGuiCol_TabHovered: return "TabHovered"; 2617 case ImGuiCol_TabActive: return "TabActive"; 2618 case ImGuiCol_TabUnfocused: return "TabUnfocused"; 2619 case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; 2620 case ImGuiCol_PlotLines: return "PlotLines"; 2621 case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; 2622 case ImGuiCol_PlotHistogram: return "PlotHistogram"; 2623 case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; 2624 case ImGuiCol_TableHeaderBg: return "TableHeaderBg"; 2625 case ImGuiCol_TableBorderStrong: return "TableBorderStrong"; 2626 case ImGuiCol_TableBorderLight: return "TableBorderLight"; 2627 case ImGuiCol_TableRowBg: return "TableRowBg"; 2628 case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt"; 2629 case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; 2630 case ImGuiCol_DragDropTarget: return "DragDropTarget"; 2631 case ImGuiCol_NavHighlight: return "NavHighlight"; 2632 case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; 2633 case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; 2634 case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; 2635 } 2636 IM_ASSERT(0); 2637 return "Unknown"; 2638 } 2639 2640 2641 //----------------------------------------------------------------------------- 2642 // [SECTION] RENDER HELPERS 2643 // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change, 2644 // we need a nicer separation between low-level functions and high-level functions relying on the ImGui context. 2645 // Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context. 2646 //----------------------------------------------------------------------------- 2647 2648 const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) 2649 { 2650 const char* text_display_end = text; 2651 if (!text_end) 2652 text_end = (const char*)-1; 2653 2654 while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) 2655 text_display_end++; 2656 return text_display_end; 2657 } 2658 2659 // Internal ImGui functions to render text 2660 // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() 2661 void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) 2662 { 2663 ImGuiContext& g = *GImGui; 2664 ImGuiWindow* window = g.CurrentWindow; 2665 2666 // Hide anything after a '##' string 2667 const char* text_display_end; 2668 if (hide_text_after_hash) 2669 { 2670 text_display_end = FindRenderedTextEnd(text, text_end); 2671 } 2672 else 2673 { 2674 if (!text_end) 2675 text_end = text + strlen(text); // FIXME-OPT 2676 text_display_end = text_end; 2677 } 2678 2679 if (text != text_display_end) 2680 { 2681 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); 2682 if (g.LogEnabled) 2683 LogRenderedText(&pos, text, text_display_end); 2684 } 2685 } 2686 2687 void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) 2688 { 2689 ImGuiContext& g = *GImGui; 2690 ImGuiWindow* window = g.CurrentWindow; 2691 2692 if (!text_end) 2693 text_end = text + strlen(text); // FIXME-OPT 2694 2695 if (text != text_end) 2696 { 2697 window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); 2698 if (g.LogEnabled) 2699 LogRenderedText(&pos, text, text_end); 2700 } 2701 } 2702 2703 // Default clip_rect uses (pos_min,pos_max) 2704 // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) 2705 void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) 2706 { 2707 // Perform CPU side clipping for single clipped element to avoid using scissor state 2708 ImVec2 pos = pos_min; 2709 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); 2710 2711 const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; 2712 const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; 2713 bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); 2714 if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min 2715 need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); 2716 2717 // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. 2718 if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); 2719 if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); 2720 2721 // Render 2722 if (need_clipping) 2723 { 2724 ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); 2725 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); 2726 } 2727 else 2728 { 2729 draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); 2730 } 2731 } 2732 2733 void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) 2734 { 2735 // Hide anything after a '##' string 2736 const char* text_display_end = FindRenderedTextEnd(text, text_end); 2737 const int text_len = (int)(text_display_end - text); 2738 if (text_len == 0) 2739 return; 2740 2741 ImGuiContext& g = *GImGui; 2742 ImGuiWindow* window = g.CurrentWindow; 2743 RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); 2744 if (g.LogEnabled) 2745 LogRenderedText(&pos_min, text, text_display_end); 2746 } 2747 2748 2749 // Another overly complex function until we reorganize everything into a nice all-in-one helper. 2750 // 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. 2751 // 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. 2752 void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) 2753 { 2754 ImGuiContext& g = *GImGui; 2755 if (text_end_full == NULL) 2756 text_end_full = FindRenderedTextEnd(text); 2757 const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); 2758 2759 //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)); 2760 //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)); 2761 //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255)); 2762 // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. 2763 if (text_size.x > pos_max.x - pos_min.x) 2764 { 2765 // Hello wo... 2766 // | | | 2767 // min max ellipsis_max 2768 // <-> this is generally some padding value 2769 2770 const ImFont* font = draw_list->_Data->Font; 2771 const float font_size = draw_list->_Data->FontSize; 2772 const char* text_end_ellipsis = NULL; 2773 2774 ImWchar ellipsis_char = font->EllipsisChar; 2775 int ellipsis_char_count = 1; 2776 if (ellipsis_char == (ImWchar)-1) 2777 { 2778 ellipsis_char = (ImWchar)'.'; 2779 ellipsis_char_count = 3; 2780 } 2781 const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); 2782 2783 float ellipsis_glyph_width = glyph->X1; // Width of the glyph with no padding on either side 2784 float ellipsis_total_width = ellipsis_glyph_width; // Full width of entire ellipsis 2785 2786 if (ellipsis_char_count > 1) 2787 { 2788 // Full ellipsis size without free spacing after it. 2789 const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize); 2790 ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots; 2791 ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots; 2792 } 2793 2794 // We can now claim the space between pos_max.x and ellipsis_max.x 2795 const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_total_width) - pos_min.x, 1.0f); 2796 float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; 2797 if (text == text_end_ellipsis && text_end_ellipsis < text_end_full) 2798 { 2799 // Always display at least 1 character if there's no room for character + ellipsis 2800 text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full); 2801 text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x; 2802 } 2803 while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) 2804 { 2805 // 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) 2806 text_end_ellipsis--; 2807 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 2808 } 2809 2810 // Render text, render ellipsis 2811 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); 2812 float ellipsis_x = pos_min.x + text_size_clipped_x; 2813 if (ellipsis_x + ellipsis_total_width <= ellipsis_max_x) 2814 for (int i = 0; i < ellipsis_char_count; i++) 2815 { 2816 font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char); 2817 ellipsis_x += ellipsis_glyph_width; 2818 } 2819 } 2820 else 2821 { 2822 RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); 2823 } 2824 2825 if (g.LogEnabled) 2826 LogRenderedText(&pos_min, text, text_end_full); 2827 } 2828 2829 // Render a rectangle shaped with optional rounding and borders 2830 void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) 2831 { 2832 ImGuiContext& g = *GImGui; 2833 ImGuiWindow* window = g.CurrentWindow; 2834 window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); 2835 const float border_size = g.Style.FrameBorderSize; 2836 if (border && border_size > 0.0f) 2837 { 2838 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); 2839 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); 2840 } 2841 } 2842 2843 void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) 2844 { 2845 ImGuiContext& g = *GImGui; 2846 ImGuiWindow* window = g.CurrentWindow; 2847 const float border_size = g.Style.FrameBorderSize; 2848 if (border_size > 0.0f) 2849 { 2850 window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); 2851 window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); 2852 } 2853 } 2854 2855 void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) 2856 { 2857 ImGuiContext& g = *GImGui; 2858 if (id != g.NavId) 2859 return; 2860 if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) 2861 return; 2862 ImGuiWindow* window = g.CurrentWindow; 2863 if (window->DC.NavHideHighlightOneFrame) 2864 return; 2865 2866 float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; 2867 ImRect display_rect = bb; 2868 display_rect.ClipWith(window->ClipRect); 2869 if (flags & ImGuiNavHighlightFlags_TypeDefault) 2870 { 2871 const float THICKNESS = 2.0f; 2872 const float DISTANCE = 3.0f + THICKNESS * 0.5f; 2873 display_rect.Expand(ImVec2(DISTANCE, DISTANCE)); 2874 bool fully_visible = window->ClipRect.Contains(display_rect); 2875 if (!fully_visible) 2876 window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); 2877 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, 0, THICKNESS); 2878 if (!fully_visible) 2879 window->DrawList->PopClipRect(); 2880 } 2881 if (flags & ImGuiNavHighlightFlags_TypeThin) 2882 { 2883 window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f); 2884 } 2885 } 2886 2887 //----------------------------------------------------------------------------- 2888 // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) 2889 //----------------------------------------------------------------------------- 2890 2891 // ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods 2892 ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(NULL) 2893 { 2894 memset(this, 0, sizeof(*this)); 2895 Name = ImStrdup(name); 2896 NameBufLen = (int)strlen(name) + 1; 2897 ID = ImHashStr(name); 2898 IDStack.push_back(ID); 2899 MoveId = GetID("#MOVE"); 2900 ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 2901 ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); 2902 AutoFitFramesX = AutoFitFramesY = -1; 2903 AutoPosLastDirection = ImGuiDir_None; 2904 SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; 2905 SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); 2906 LastFrameActive = -1; 2907 LastTimeActive = -1.0f; 2908 FontWindowScale = 1.0f; 2909 SettingsOffset = -1; 2910 DrawList = &DrawListInst; 2911 DrawList->_Data = &context->DrawListSharedData; 2912 DrawList->_OwnerName = Name; 2913 } 2914 2915 ImGuiWindow::~ImGuiWindow() 2916 { 2917 IM_ASSERT(DrawList == &DrawListInst); 2918 IM_DELETE(Name); 2919 for (int i = 0; i != ColumnsStorage.Size; i++) 2920 ColumnsStorage[i].~ImGuiOldColumns(); 2921 } 2922 2923 ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) 2924 { 2925 ImGuiID seed = IDStack.back(); 2926 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 2927 ImGui::KeepAliveID(id); 2928 #ifdef IMGUI_ENABLE_TEST_ENGINE 2929 ImGuiContext& g = *GImGui; 2930 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); 2931 #endif 2932 return id; 2933 } 2934 2935 ImGuiID ImGuiWindow::GetID(const void* ptr) 2936 { 2937 ImGuiID seed = IDStack.back(); 2938 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); 2939 ImGui::KeepAliveID(id); 2940 #ifdef IMGUI_ENABLE_TEST_ENGINE 2941 ImGuiContext& g = *GImGui; 2942 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); 2943 #endif 2944 return id; 2945 } 2946 2947 ImGuiID ImGuiWindow::GetID(int n) 2948 { 2949 ImGuiID seed = IDStack.back(); 2950 ImGuiID id = ImHashData(&n, sizeof(n), seed); 2951 ImGui::KeepAliveID(id); 2952 #ifdef IMGUI_ENABLE_TEST_ENGINE 2953 ImGuiContext& g = *GImGui; 2954 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); 2955 #endif 2956 return id; 2957 } 2958 2959 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) 2960 { 2961 ImGuiID seed = IDStack.back(); 2962 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 2963 #ifdef IMGUI_ENABLE_TEST_ENGINE 2964 ImGuiContext& g = *GImGui; 2965 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); 2966 #endif 2967 return id; 2968 } 2969 2970 ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) 2971 { 2972 ImGuiID seed = IDStack.back(); 2973 ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); 2974 #ifdef IMGUI_ENABLE_TEST_ENGINE 2975 ImGuiContext& g = *GImGui; 2976 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); 2977 #endif 2978 return id; 2979 } 2980 2981 ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) 2982 { 2983 ImGuiID seed = IDStack.back(); 2984 ImGuiID id = ImHashData(&n, sizeof(n), seed); 2985 #ifdef IMGUI_ENABLE_TEST_ENGINE 2986 ImGuiContext& g = *GImGui; 2987 IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); 2988 #endif 2989 return id; 2990 } 2991 2992 // This is only used in rare/specific situations to manufacture an ID out of nowhere. 2993 ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) 2994 { 2995 ImGuiID seed = IDStack.back(); 2996 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) }; 2997 ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); 2998 ImGui::KeepAliveID(id); 2999 return id; 3000 } 3001 3002 static void SetCurrentWindow(ImGuiWindow* window) 3003 { 3004 ImGuiContext& g = *GImGui; 3005 g.CurrentWindow = window; 3006 g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; 3007 if (window) 3008 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 3009 } 3010 3011 void ImGui::GcCompactTransientMiscBuffers() 3012 { 3013 ImGuiContext& g = *GImGui; 3014 g.ItemFlagsStack.clear(); 3015 g.GroupStack.clear(); 3016 TableGcCompactSettings(); 3017 } 3018 3019 // Free up/compact internal window buffers, we can use this when a window becomes unused. 3020 // Not freed: 3021 // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data) 3022 // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. 3023 void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) 3024 { 3025 window->MemoryCompacted = true; 3026 window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; 3027 window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; 3028 window->IDStack.clear(); 3029 window->DrawList->_ClearFreeMemory(); 3030 window->DC.ChildWindows.clear(); 3031 window->DC.ItemWidthStack.clear(); 3032 window->DC.TextWrapPosStack.clear(); 3033 } 3034 3035 void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window) 3036 { 3037 // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening. 3038 // The other buffers tends to amortize much faster. 3039 window->MemoryCompacted = false; 3040 window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity); 3041 window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity); 3042 window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0; 3043 } 3044 3045 void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) 3046 { 3047 ImGuiContext& g = *GImGui; 3048 g.ActiveIdIsJustActivated = (g.ActiveId != id); 3049 if (g.ActiveIdIsJustActivated) 3050 { 3051 g.ActiveIdTimer = 0.0f; 3052 g.ActiveIdHasBeenPressedBefore = false; 3053 g.ActiveIdHasBeenEditedBefore = false; 3054 g.ActiveIdMouseButton = -1; 3055 if (id != 0) 3056 { 3057 g.LastActiveId = id; 3058 g.LastActiveIdTimer = 0.0f; 3059 } 3060 } 3061 g.ActiveId = id; 3062 g.ActiveIdAllowOverlap = false; 3063 g.ActiveIdNoClearOnFocusLoss = false; 3064 g.ActiveIdWindow = window; 3065 g.ActiveIdHasBeenEditedThisFrame = false; 3066 if (id) 3067 { 3068 g.ActiveIdIsAlive = id; 3069 g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; 3070 } 3071 3072 // Clear declaration of inputs claimed by the widget 3073 // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) 3074 g.ActiveIdUsingMouseWheel = false; 3075 g.ActiveIdUsingNavDirMask = 0x00; 3076 g.ActiveIdUsingNavInputMask = 0x00; 3077 g.ActiveIdUsingKeyInputMask = 0x00; 3078 } 3079 3080 void ImGui::ClearActiveID() 3081 { 3082 SetActiveID(0, NULL); // g.ActiveId = 0; 3083 } 3084 3085 void ImGui::SetHoveredID(ImGuiID id) 3086 { 3087 ImGuiContext& g = *GImGui; 3088 g.HoveredId = id; 3089 g.HoveredIdAllowOverlap = false; 3090 g.HoveredIdUsingMouseWheel = false; 3091 if (id != 0 && g.HoveredIdPreviousFrame != id) 3092 g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; 3093 } 3094 3095 ImGuiID ImGui::GetHoveredID() 3096 { 3097 ImGuiContext& g = *GImGui; 3098 return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; 3099 } 3100 3101 void ImGui::KeepAliveID(ImGuiID id) 3102 { 3103 ImGuiContext& g = *GImGui; 3104 if (g.ActiveId == id) 3105 g.ActiveIdIsAlive = id; 3106 if (g.ActiveIdPreviousFrame == id) 3107 g.ActiveIdPreviousFrameIsAlive = true; 3108 } 3109 3110 void ImGui::MarkItemEdited(ImGuiID id) 3111 { 3112 // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). 3113 // 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. 3114 ImGuiContext& g = *GImGui; 3115 IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); 3116 IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. 3117 //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); 3118 g.ActiveIdHasBeenEditedThisFrame = true; 3119 g.ActiveIdHasBeenEditedBefore = true; 3120 g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; 3121 } 3122 3123 static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) 3124 { 3125 // An active popup disable hovering on other windows (apart from its own children) 3126 // FIXME-OPT: This could be cached/stored within the window. 3127 ImGuiContext& g = *GImGui; 3128 if (g.NavWindow) 3129 if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) 3130 if (focused_root_window->WasActive && focused_root_window != window->RootWindow) 3131 { 3132 // For the purpose of those flags we differentiate "standard popup" from "modal popup" 3133 // NB: The order of those two tests is important because Modal windows are also Popups. 3134 if (focused_root_window->Flags & ImGuiWindowFlags_Modal) 3135 return false; 3136 if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 3137 return false; 3138 } 3139 return true; 3140 } 3141 3142 // This is roughly matching the behavior of internal-facing ItemHoverable() 3143 // - 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() 3144 // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId 3145 bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) 3146 { 3147 ImGuiContext& g = *GImGui; 3148 ImGuiWindow* window = g.CurrentWindow; 3149 if (g.NavDisableMouseHover && !g.NavDisableHighlight) 3150 return IsItemFocused(); 3151 3152 // Test for bounding box overlap, as updated as ItemAdd() 3153 ImGuiItemStatusFlags status_flags = window->DC.LastItemStatusFlags; 3154 if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) 3155 return false; 3156 IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function 3157 3158 // Test if we are hovering the right window (our window could be behind another window) 3159 // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) 3160 // [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 3161 // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was 3162 // the test that has been running for a long while. 3163 if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) 3164 if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) 3165 return false; 3166 3167 // Test if another item is active (e.g. being dragged) 3168 if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) 3169 if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) 3170 return false; 3171 3172 // Test if interactions on this window are blocked by an active popup or modal. 3173 // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. 3174 if (!IsWindowContentHoverable(window, flags)) 3175 return false; 3176 3177 // Test if the item is disabled 3178 if ((g.CurrentItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) 3179 return false; 3180 3181 // Special handling for calling after Begin() which represent the title bar or tab. 3182 // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. 3183 if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) 3184 return false; 3185 return true; 3186 } 3187 3188 // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). 3189 bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) 3190 { 3191 ImGuiContext& g = *GImGui; 3192 if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) 3193 return false; 3194 3195 ImGuiWindow* window = g.CurrentWindow; 3196 if (g.HoveredWindow != window) 3197 return false; 3198 if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) 3199 return false; 3200 if (!IsMouseHoveringRect(bb.Min, bb.Max)) 3201 return false; 3202 if (g.NavDisableMouseHover) 3203 return false; 3204 if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (g.CurrentItemFlags & ImGuiItemFlags_Disabled)) 3205 { 3206 g.HoveredIdDisabled = true; 3207 return false; 3208 } 3209 3210 // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level 3211 // hover test in widgets code. We could also decide to split this function is two. 3212 if (id != 0) 3213 { 3214 SetHoveredID(id); 3215 3216 // [DEBUG] Item Picker tool! 3217 // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making 3218 // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered 3219 // items if we perform the test in ItemAdd(), but that would incur a small runtime cost. 3220 // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd(). 3221 if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) 3222 GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); 3223 if (g.DebugItemPickerBreakId == id) 3224 IM_DEBUG_BREAK(); 3225 } 3226 3227 return true; 3228 } 3229 3230 bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) 3231 { 3232 ImGuiContext& g = *GImGui; 3233 ImGuiWindow* window = g.CurrentWindow; 3234 if (!bb.Overlaps(window->ClipRect)) 3235 if (id == 0 || (id != g.ActiveId && id != g.NavId)) 3236 if (clip_even_when_logged || !g.LogEnabled) 3237 return true; 3238 return false; 3239 } 3240 3241 // This is also inlined in ItemAdd() 3242 // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! 3243 void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) 3244 { 3245 window->DC.LastItemId = item_id; 3246 window->DC.LastItemStatusFlags = item_flags; 3247 window->DC.LastItemRect = item_rect; 3248 } 3249 3250 // Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. 3251 void ImGui::ItemFocusable(ImGuiWindow* window, ImGuiID id) 3252 { 3253 ImGuiContext& g = *GImGui; 3254 IM_ASSERT(id != 0 && id == window->DC.LastItemId); 3255 3256 // Increment counters 3257 // FIXME: ImGuiItemFlags_Disabled should disable more. 3258 const bool is_tab_stop = (g.CurrentItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; 3259 window->DC.FocusCounterRegular++; 3260 if (is_tab_stop) 3261 { 3262 window->DC.FocusCounterTabStop++; 3263 if (g.NavId == id) 3264 g.NavIdTabCounter = window->DC.FocusCounterTabStop; 3265 } 3266 3267 // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. 3268 // (Note that we can always TAB out of a widget that doesn't allow tabbing in) 3269 if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL) 3270 { 3271 g.TabFocusRequestNextWindow = window; 3272 g.TabFocusRequestNextCounterTabStop = 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. 3273 } 3274 3275 // Handle focus requests 3276 if (g.TabFocusRequestCurrWindow == window) 3277 { 3278 if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular) 3279 { 3280 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_FocusedByCode; 3281 return; 3282 } 3283 if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) 3284 { 3285 g.NavJustTabbedId = id; 3286 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_FocusedByTabbing; 3287 return; 3288 } 3289 3290 // If another item is about to be focused, we clear our own active id 3291 if (g.ActiveId == id) 3292 ClearActiveID(); 3293 } 3294 } 3295 3296 float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) 3297 { 3298 if (wrap_pos_x < 0.0f) 3299 return 0.0f; 3300 3301 ImGuiContext& g = *GImGui; 3302 ImGuiWindow* window = g.CurrentWindow; 3303 if (wrap_pos_x == 0.0f) 3304 { 3305 // We could decide to setup a default wrapping max point for auto-resizing windows, 3306 // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function? 3307 //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) 3308 // wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x); 3309 //else 3310 wrap_pos_x = window->WorkRect.Max.x; 3311 } 3312 else if (wrap_pos_x > 0.0f) 3313 { 3314 wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space 3315 } 3316 3317 return ImMax(wrap_pos_x - pos.x, 1.0f); 3318 } 3319 3320 // IM_ALLOC() == ImGui::MemAlloc() 3321 void* ImGui::MemAlloc(size_t size) 3322 { 3323 if (ImGuiContext* ctx = GImGui) 3324 ctx->IO.MetricsActiveAllocations++; 3325 return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); 3326 } 3327 3328 // IM_FREE() == ImGui::MemFree() 3329 void ImGui::MemFree(void* ptr) 3330 { 3331 if (ptr) 3332 if (ImGuiContext* ctx = GImGui) 3333 ctx->IO.MetricsActiveAllocations--; 3334 return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData); 3335 } 3336 3337 const char* ImGui::GetClipboardText() 3338 { 3339 ImGuiContext& g = *GImGui; 3340 return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : ""; 3341 } 3342 3343 void ImGui::SetClipboardText(const char* text) 3344 { 3345 ImGuiContext& g = *GImGui; 3346 if (g.IO.SetClipboardTextFn) 3347 g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text); 3348 } 3349 3350 const char* ImGui::GetVersion() 3351 { 3352 return IMGUI_VERSION; 3353 } 3354 3355 // Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself 3356 // 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 3357 ImGuiContext* ImGui::GetCurrentContext() 3358 { 3359 return GImGui; 3360 } 3361 3362 void ImGui::SetCurrentContext(ImGuiContext* ctx) 3363 { 3364 #ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC 3365 IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. 3366 #else 3367 GImGui = ctx; 3368 #endif 3369 } 3370 3371 void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) 3372 { 3373 GImAllocatorAllocFunc = alloc_func; 3374 GImAllocatorFreeFunc = free_func; 3375 GImAllocatorUserData = user_data; 3376 } 3377 3378 // This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) 3379 void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) 3380 { 3381 *p_alloc_func = GImAllocatorAllocFunc; 3382 *p_free_func = GImAllocatorFreeFunc; 3383 *p_user_data = GImAllocatorUserData; 3384 } 3385 3386 ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) 3387 { 3388 ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); 3389 if (GImGui == NULL) 3390 SetCurrentContext(ctx); 3391 Initialize(ctx); 3392 return ctx; 3393 } 3394 3395 void ImGui::DestroyContext(ImGuiContext* ctx) 3396 { 3397 if (ctx == NULL) 3398 ctx = GImGui; 3399 Shutdown(ctx); 3400 if (GImGui == ctx) 3401 SetCurrentContext(NULL); 3402 IM_DELETE(ctx); 3403 } 3404 3405 // No specific ordering/dependency support, will see as needed 3406 ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) 3407 { 3408 ImGuiContext& g = *ctx; 3409 IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); 3410 g.Hooks.push_back(*hook); 3411 g.Hooks.back().HookId = ++g.HookIdNext; 3412 return g.HookIdNext; 3413 } 3414 3415 // Deferred removal, avoiding issue with changing vector while iterating it 3416 void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) 3417 { 3418 ImGuiContext& g = *ctx; 3419 IM_ASSERT(hook_id != 0); 3420 for (int n = 0; n < g.Hooks.Size; n++) 3421 if (g.Hooks[n].HookId == hook_id) 3422 g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; 3423 } 3424 3425 // Call context hooks (used by e.g. test engine) 3426 // We assume a small number of hooks so all stored in same array 3427 void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) 3428 { 3429 ImGuiContext& g = *ctx; 3430 for (int n = 0; n < g.Hooks.Size; n++) 3431 if (g.Hooks[n].Type == hook_type) 3432 g.Hooks[n].Callback(&g, &g.Hooks[n]); 3433 } 3434 3435 ImGuiIO& ImGui::GetIO() 3436 { 3437 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 3438 return GImGui->IO; 3439 } 3440 3441 // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame() 3442 ImDrawData* ImGui::GetDrawData() 3443 { 3444 ImGuiContext& g = *GImGui; 3445 ImGuiViewportP* viewport = g.Viewports[0]; 3446 return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL; 3447 } 3448 3449 double ImGui::GetTime() 3450 { 3451 return GImGui->Time; 3452 } 3453 3454 int ImGui::GetFrameCount() 3455 { 3456 return GImGui->FrameCount; 3457 } 3458 3459 static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) 3460 { 3461 // Create the draw list on demand, because they are not frequently used for all viewports 3462 ImGuiContext& g = *GImGui; 3463 IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); 3464 ImDrawList* draw_list = viewport->DrawLists[drawlist_no]; 3465 if (draw_list == NULL) 3466 { 3467 draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData); 3468 draw_list->_OwnerName = drawlist_name; 3469 viewport->DrawLists[drawlist_no] = draw_list; 3470 } 3471 3472 // Our ImDrawList system requires that there is always a command 3473 if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount) 3474 { 3475 draw_list->_ResetForNewFrame(); 3476 draw_list->PushTextureID(g.IO.Fonts->TexID); 3477 draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); 3478 viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount; 3479 } 3480 return draw_list; 3481 } 3482 3483 ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) 3484 { 3485 return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background"); 3486 } 3487 3488 ImDrawList* ImGui::GetBackgroundDrawList() 3489 { 3490 ImGuiContext& g = *GImGui; 3491 return GetBackgroundDrawList(g.Viewports[0]); 3492 } 3493 3494 ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) 3495 { 3496 return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); 3497 } 3498 3499 ImDrawList* ImGui::GetForegroundDrawList() 3500 { 3501 ImGuiContext& g = *GImGui; 3502 return GetForegroundDrawList(g.Viewports[0]); 3503 } 3504 3505 ImDrawListSharedData* ImGui::GetDrawListSharedData() 3506 { 3507 return &GImGui->DrawListSharedData; 3508 } 3509 3510 void ImGui::StartMouseMovingWindow(ImGuiWindow* window) 3511 { 3512 // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. 3513 // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. 3514 // This is because we want ActiveId to be set even when the window is not permitted to move. 3515 ImGuiContext& g = *GImGui; 3516 FocusWindow(window); 3517 SetActiveID(window->MoveId, window); 3518 g.NavDisableHighlight = true; 3519 g.ActiveIdNoClearOnFocusLoss = true; 3520 g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; 3521 3522 bool can_move_window = true; 3523 if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) 3524 can_move_window = false; 3525 if (can_move_window) 3526 g.MovingWindow = window; 3527 } 3528 3529 // Handle mouse moving window 3530 // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() 3531 // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId. 3532 // This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs, 3533 // but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other. 3534 void ImGui::UpdateMouseMovingWindowNewFrame() 3535 { 3536 ImGuiContext& g = *GImGui; 3537 if (g.MovingWindow != NULL) 3538 { 3539 // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). 3540 // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. 3541 KeepAliveID(g.ActiveId); 3542 IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); 3543 ImGuiWindow* moving_window = g.MovingWindow->RootWindow; 3544 if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) 3545 { 3546 ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; 3547 if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) 3548 { 3549 MarkIniSettingsDirty(moving_window); 3550 SetWindowPos(moving_window, pos, ImGuiCond_Always); 3551 } 3552 FocusWindow(g.MovingWindow); 3553 } 3554 else 3555 { 3556 ClearActiveID(); 3557 g.MovingWindow = NULL; 3558 } 3559 } 3560 else 3561 { 3562 // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. 3563 if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) 3564 { 3565 KeepAliveID(g.ActiveId); 3566 if (!g.IO.MouseDown[0]) 3567 ClearActiveID(); 3568 } 3569 } 3570 } 3571 3572 // Initiate moving window when clicking on empty space or title bar. 3573 // Handle left-click and right-click focus. 3574 void ImGui::UpdateMouseMovingWindowEndFrame() 3575 { 3576 ImGuiContext& g = *GImGui; 3577 if (g.ActiveId != 0 || g.HoveredId != 0) 3578 return; 3579 3580 // Unless we just made a window/popup appear 3581 if (g.NavWindow && g.NavWindow->Appearing) 3582 return; 3583 3584 // Click on empty space to focus window and start moving 3585 // (after we're done with all our widgets) 3586 if (g.IO.MouseClicked[0]) 3587 { 3588 // Handle the edge case of a popup being closed while clicking in its empty space. 3589 // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. 3590 ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; 3591 const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); 3592 3593 if (root_window != NULL && !is_closed_popup) 3594 { 3595 StartMouseMovingWindow(g.HoveredWindow); //-V595 3596 3597 // Cancel moving if clicked outside of title bar 3598 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) 3599 if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) 3600 g.MovingWindow = NULL; 3601 3602 // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already) 3603 if (g.HoveredIdDisabled) 3604 g.MovingWindow = NULL; 3605 } 3606 else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) 3607 { 3608 // Clicking on void disable focus 3609 FocusWindow(NULL); 3610 } 3611 } 3612 3613 // With right mouse button we close popups without changing focus based on where the mouse is aimed 3614 // Instead, focus will be restored to the window under the bottom-most closed popup. 3615 // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) 3616 if (g.IO.MouseClicked[1]) 3617 { 3618 // Find the top-most window between HoveredWindow and the top-most Modal Window. 3619 // This is where we can trim the popup stack. 3620 ImGuiWindow* modal = GetTopMostPopupModal(); 3621 bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); 3622 ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); 3623 } 3624 } 3625 3626 static bool IsWindowActiveAndVisible(ImGuiWindow* window) 3627 { 3628 return (window->Active) && (!window->Hidden); 3629 } 3630 3631 static void ImGui::UpdateMouseInputs() 3632 { 3633 ImGuiContext& g = *GImGui; 3634 3635 // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) 3636 if (IsMousePosValid(&g.IO.MousePos)) 3637 g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); 3638 3639 // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta 3640 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) 3641 g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; 3642 else 3643 g.IO.MouseDelta = ImVec2(0.0f, 0.0f); 3644 if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) 3645 g.NavDisableMouseHover = false; 3646 3647 g.IO.MousePosPrev = g.IO.MousePos; 3648 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3649 { 3650 g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; 3651 g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; 3652 g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; 3653 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; 3654 g.IO.MouseDoubleClicked[i] = false; 3655 if (g.IO.MouseClicked[i]) 3656 { 3657 if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) 3658 { 3659 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); 3660 if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) 3661 g.IO.MouseDoubleClicked[i] = true; 3662 g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click 3663 } 3664 else 3665 { 3666 g.IO.MouseClickedTime[i] = g.Time; 3667 } 3668 g.IO.MouseClickedPos[i] = g.IO.MousePos; 3669 g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; 3670 g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); 3671 g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; 3672 } 3673 else if (g.IO.MouseDown[i]) 3674 { 3675 // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold 3676 ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); 3677 g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); 3678 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); 3679 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); 3680 } 3681 if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) 3682 g.IO.MouseDownWasDoubleClick[i] = false; 3683 if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation 3684 g.NavDisableMouseHover = false; 3685 } 3686 } 3687 3688 static void StartLockWheelingWindow(ImGuiWindow* window) 3689 { 3690 ImGuiContext& g = *GImGui; 3691 if (g.WheelingWindow == window) 3692 return; 3693 g.WheelingWindow = window; 3694 g.WheelingWindowRefMousePos = g.IO.MousePos; 3695 g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; 3696 } 3697 3698 void ImGui::UpdateMouseWheel() 3699 { 3700 ImGuiContext& g = *GImGui; 3701 3702 // Reset the locked window if we move the mouse or after the timer elapses 3703 if (g.WheelingWindow != NULL) 3704 { 3705 g.WheelingWindowTimer -= g.IO.DeltaTime; 3706 if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) 3707 g.WheelingWindowTimer = 0.0f; 3708 if (g.WheelingWindowTimer <= 0.0f) 3709 { 3710 g.WheelingWindow = NULL; 3711 g.WheelingWindowTimer = 0.0f; 3712 } 3713 } 3714 3715 if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f) 3716 return; 3717 3718 if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel)) 3719 return; 3720 3721 ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; 3722 if (!window || window->Collapsed) 3723 return; 3724 3725 // Zoom / Scale window 3726 // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. 3727 if (g.IO.MouseWheel != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) 3728 { 3729 StartLockWheelingWindow(window); 3730 const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); 3731 const float scale = new_font_scale / window->FontWindowScale; 3732 window->FontWindowScale = new_font_scale; 3733 if (window == window->RootWindow) 3734 { 3735 const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; 3736 SetWindowPos(window, window->Pos + offset, 0); 3737 window->Size = ImFloor(window->Size * scale); 3738 window->SizeFull = ImFloor(window->SizeFull * scale); 3739 } 3740 return; 3741 } 3742 3743 // Mouse wheel scrolling 3744 // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent 3745 if (g.IO.KeyCtrl) 3746 return; 3747 3748 // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead 3749 // (we avoid doing it on OSX as it the OS input layer handles this already) 3750 const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; 3751 const float wheel_y = swap_axis ? 0.0f : g.IO.MouseWheel; 3752 const float wheel_x = swap_axis ? g.IO.MouseWheel : g.IO.MouseWheelH; 3753 3754 // Vertical Mouse Wheel scrolling 3755 if (wheel_y != 0.0f) 3756 { 3757 StartLockWheelingWindow(window); 3758 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) 3759 window = window->ParentWindow; 3760 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) 3761 { 3762 float max_step = window->InnerRect.GetHeight() * 0.67f; 3763 float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); 3764 SetScrollY(window, window->Scroll.y - wheel_y * scroll_step); 3765 } 3766 } 3767 3768 // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held 3769 if (wheel_x != 0.0f) 3770 { 3771 StartLockWheelingWindow(window); 3772 while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) 3773 window = window->ParentWindow; 3774 if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) 3775 { 3776 float max_step = window->InnerRect.GetWidth() * 0.67f; 3777 float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); 3778 SetScrollX(window, window->Scroll.x - wheel_x * scroll_step); 3779 } 3780 } 3781 } 3782 3783 void ImGui::UpdateTabFocus() 3784 { 3785 ImGuiContext& g = *GImGui; 3786 3787 // Pressing TAB activate widget focus 3788 g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); 3789 if (g.ActiveId == 0 && g.TabFocusPressed) 3790 { 3791 // - This path is only taken when no widget are active/tabbed-into yet. 3792 // Subsequent tabbing will be processed by FocusableItemRegister() 3793 // - Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also 3794 // manipulate the Next fields here even though they will be turned into Curr fields below. 3795 g.TabFocusRequestNextWindow = g.NavWindow; 3796 g.TabFocusRequestNextCounterRegular = INT_MAX; 3797 if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) 3798 g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + (g.IO.KeyShift ? -1 : 0); 3799 else 3800 g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; 3801 } 3802 3803 // Turn queued focus request into current one 3804 g.TabFocusRequestCurrWindow = NULL; 3805 g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX; 3806 if (g.TabFocusRequestNextWindow != NULL) 3807 { 3808 ImGuiWindow* window = g.TabFocusRequestNextWindow; 3809 g.TabFocusRequestCurrWindow = window; 3810 if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) 3811 g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); 3812 if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) 3813 g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); 3814 g.TabFocusRequestNextWindow = NULL; 3815 g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX; 3816 } 3817 3818 g.NavIdTabCounter = INT_MAX; 3819 } 3820 3821 // 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) 3822 void ImGui::UpdateHoveredWindowAndCaptureFlags() 3823 { 3824 ImGuiContext& g = *GImGui; 3825 g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); 3826 3827 // Find the window hovered by mouse: 3828 // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. 3829 // - 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. 3830 // - 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. 3831 bool clear_hovered_windows = false; 3832 FindHoveredWindow(); 3833 3834 // Modal windows prevents mouse from hovering behind them. 3835 ImGuiWindow* modal_window = GetTopMostPopupModal(); 3836 if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window)) 3837 clear_hovered_windows = true; 3838 3839 // Disabled mouse? 3840 if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) 3841 clear_hovered_windows = true; 3842 3843 // 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. 3844 int mouse_earliest_button_down = -1; 3845 bool mouse_any_down = false; 3846 for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) 3847 { 3848 if (g.IO.MouseClicked[i]) 3849 g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0); 3850 mouse_any_down |= g.IO.MouseDown[i]; 3851 if (g.IO.MouseDown[i]) 3852 if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) 3853 mouse_earliest_button_down = i; 3854 } 3855 const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; 3856 3857 // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. 3858 // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) 3859 const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; 3860 if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) 3861 clear_hovered_windows = true; 3862 3863 if (clear_hovered_windows) 3864 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; 3865 3866 // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) 3867 if (g.WantCaptureMouseNextFrame != -1) 3868 g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); 3869 else 3870 g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0); 3871 3872 // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) 3873 if (g.WantCaptureKeyboardNextFrame != -1) 3874 g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); 3875 else 3876 g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); 3877 if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) 3878 g.IO.WantCaptureKeyboard = true; 3879 3880 // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible 3881 g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; 3882 } 3883 3884 ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() 3885 { 3886 ImGuiContext& g = *GImGui; 3887 ImGuiKeyModFlags key_mod_flags = ImGuiKeyModFlags_None; 3888 if (g.IO.KeyCtrl) { key_mod_flags |= ImGuiKeyModFlags_Ctrl; } 3889 if (g.IO.KeyShift) { key_mod_flags |= ImGuiKeyModFlags_Shift; } 3890 if (g.IO.KeyAlt) { key_mod_flags |= ImGuiKeyModFlags_Alt; } 3891 if (g.IO.KeySuper) { key_mod_flags |= ImGuiKeyModFlags_Super; } 3892 return key_mod_flags; 3893 } 3894 3895 void ImGui::NewFrame() 3896 { 3897 IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); 3898 ImGuiContext& g = *GImGui; 3899 3900 // Remove pending delete hooks before frame start. 3901 // This deferred removal avoid issues of removal while iterating the hook vector 3902 for (int n = g.Hooks.Size - 1; n >= 0; n--) 3903 if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_) 3904 g.Hooks.erase(&g.Hooks[n]); 3905 3906 CallContextHooks(&g, ImGuiContextHookType_NewFramePre); 3907 3908 // Check and assert for various common IO and Configuration mistakes 3909 ErrorCheckNewFrameSanityChecks(); 3910 3911 // Load settings on first frame, save settings when modified (after a delay) 3912 UpdateSettings(); 3913 3914 g.Time += g.IO.DeltaTime; 3915 g.WithinFrameScope = true; 3916 g.FrameCount += 1; 3917 g.TooltipOverrideCount = 0; 3918 g.WindowsActiveCount = 0; 3919 g.MenusIdSubmittedThisFrame.resize(0); 3920 3921 // Calculate frame-rate for the user, as a purely luxurious feature 3922 g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; 3923 g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; 3924 g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); 3925 g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); 3926 g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; 3927 3928 UpdateViewportsNewFrame(); 3929 3930 // Setup current font and draw list shared data 3931 g.IO.Fonts->Locked = true; 3932 SetCurrentFont(GetDefaultFont()); 3933 IM_ASSERT(g.Font->IsLoaded()); 3934 ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); 3935 for (int n = 0; n < g.Viewports.Size; n++) 3936 virtual_space.Add(g.Viewports[n]->GetMainRect()); 3937 g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); 3938 g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; 3939 g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); 3940 g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; 3941 if (g.Style.AntiAliasedLines) 3942 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; 3943 if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines)) 3944 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex; 3945 if (g.Style.AntiAliasedFill) 3946 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; 3947 if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) 3948 g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; 3949 3950 // Mark rendering data as invalid to prevent user who may have a handle on it to use it. 3951 for (int n = 0; n < g.Viewports.Size; n++) 3952 { 3953 ImGuiViewportP* viewport = g.Viewports[n]; 3954 viewport->DrawDataP.Clear(); 3955 } 3956 3957 // Drag and drop keep the source ID alive so even if the source disappear our state is consistent 3958 if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) 3959 KeepAliveID(g.DragDropPayload.SourceId); 3960 3961 // Update HoveredId data 3962 if (!g.HoveredIdPreviousFrame) 3963 g.HoveredIdTimer = 0.0f; 3964 if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) 3965 g.HoveredIdNotActiveTimer = 0.0f; 3966 if (g.HoveredId) 3967 g.HoveredIdTimer += g.IO.DeltaTime; 3968 if (g.HoveredId && g.ActiveId != g.HoveredId) 3969 g.HoveredIdNotActiveTimer += g.IO.DeltaTime; 3970 g.HoveredIdPreviousFrame = g.HoveredId; 3971 g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel; 3972 g.HoveredId = 0; 3973 g.HoveredIdAllowOverlap = false; 3974 g.HoveredIdUsingMouseWheel = false; 3975 g.HoveredIdDisabled = false; 3976 3977 // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore) 3978 if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) 3979 ClearActiveID(); 3980 if (g.ActiveId) 3981 g.ActiveIdTimer += g.IO.DeltaTime; 3982 g.LastActiveIdTimer += g.IO.DeltaTime; 3983 g.ActiveIdPreviousFrame = g.ActiveId; 3984 g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow; 3985 g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore; 3986 g.ActiveIdIsAlive = 0; 3987 g.ActiveIdHasBeenEditedThisFrame = false; 3988 g.ActiveIdPreviousFrameIsAlive = false; 3989 g.ActiveIdIsJustActivated = false; 3990 if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) 3991 g.TempInputId = 0; 3992 if (g.ActiveId == 0) 3993 { 3994 g.ActiveIdUsingNavDirMask = 0x00; 3995 g.ActiveIdUsingNavInputMask = 0x00; 3996 g.ActiveIdUsingKeyInputMask = 0x00; 3997 } 3998 3999 // Drag and drop 4000 g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; 4001 g.DragDropAcceptIdCurr = 0; 4002 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 4003 g.DragDropWithinSource = false; 4004 g.DragDropWithinTarget = false; 4005 g.DragDropHoldJustPressedId = 0; 4006 4007 // Update keyboard input state 4008 // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools 4009 g.IO.KeyMods = GetMergedKeyModFlags(); 4010 memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); 4011 for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) 4012 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; 4013 4014 // Update gamepad/keyboard navigation 4015 NavUpdate(); 4016 4017 // Update mouse input state 4018 UpdateMouseInputs(); 4019 4020 // Find hovered window 4021 // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) 4022 UpdateHoveredWindowAndCaptureFlags(); 4023 4024 // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) 4025 UpdateMouseMovingWindowNewFrame(); 4026 4027 // Background darkening/whitening 4028 if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) 4029 g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); 4030 else 4031 g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); 4032 4033 g.MouseCursor = ImGuiMouseCursor_Arrow; 4034 g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; 4035 g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default 4036 4037 // Mouse wheel scrolling, scale 4038 UpdateMouseWheel(); 4039 4040 // Update legacy TAB focus 4041 UpdateTabFocus(); 4042 4043 // Mark all windows as not visible and compact unused memory. 4044 IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); 4045 const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; 4046 for (int i = 0; i != g.Windows.Size; i++) 4047 { 4048 ImGuiWindow* window = g.Windows[i]; 4049 window->WasActive = window->Active; 4050 window->BeginCount = 0; 4051 window->Active = false; 4052 window->WriteAccessed = false; 4053 4054 // Garbage collect transient buffers of recently unused windows 4055 if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) 4056 GcCompactTransientWindowBuffers(window); 4057 } 4058 4059 // Garbage collect transient buffers of recently unused tables 4060 for (int i = 0; i < g.TablesLastTimeActive.Size; i++) 4061 if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) 4062 TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); 4063 for (int i = 0; i < g.TablesTempDataStack.Size; i++) 4064 if (g.TablesTempDataStack[i].LastTimeActive >= 0.0f && g.TablesTempDataStack[i].LastTimeActive < memory_compact_start_time) 4065 TableGcCompactTransientBuffers(&g.TablesTempDataStack[i]); 4066 if (g.GcCompactAll) 4067 GcCompactTransientMiscBuffers(); 4068 g.GcCompactAll = false; 4069 4070 // Closing the focused window restore focus to the first active root window in descending z-order 4071 if (g.NavWindow && !g.NavWindow->WasActive) 4072 FocusTopMostWindowUnderOne(NULL, NULL); 4073 4074 // No window should be open at the beginning of the frame. 4075 // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. 4076 g.CurrentWindowStack.resize(0); 4077 g.BeginPopupStack.resize(0); 4078 g.ItemFlagsStack.resize(0); 4079 g.ItemFlagsStack.push_back(ImGuiItemFlags_None); 4080 g.GroupStack.resize(0); 4081 ClosePopupsOverWindow(g.NavWindow, false); 4082 4083 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. 4084 UpdateDebugToolItemPicker(); 4085 4086 // Create implicit/fallback window - which we will only render it if the user has added something to it. 4087 // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. 4088 // This fallback is particularly important as it avoid ImGui:: calls from crashing. 4089 g.WithinFrameScopeWithImplicitWindow = true; 4090 SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); 4091 Begin("Debug##Default"); 4092 IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); 4093 4094 CallContextHooks(&g, ImGuiContextHookType_NewFramePost); 4095 } 4096 4097 // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. 4098 void ImGui::UpdateDebugToolItemPicker() 4099 { 4100 ImGuiContext& g = *GImGui; 4101 g.DebugItemPickerBreakId = 0; 4102 if (g.DebugItemPickerActive) 4103 { 4104 const ImGuiID hovered_id = g.HoveredIdPreviousFrame; 4105 SetMouseCursor(ImGuiMouseCursor_Hand); 4106 if (IsKeyPressedMap(ImGuiKey_Escape)) 4107 g.DebugItemPickerActive = false; 4108 if (IsMouseClicked(0) && hovered_id) 4109 { 4110 g.DebugItemPickerBreakId = hovered_id; 4111 g.DebugItemPickerActive = false; 4112 } 4113 SetNextWindowBgAlpha(0.60f); 4114 BeginTooltip(); 4115 Text("HoveredId: 0x%08X", hovered_id); 4116 Text("Press ESC to abort picking."); 4117 TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); 4118 EndTooltip(); 4119 } 4120 } 4121 4122 void ImGui::Initialize(ImGuiContext* context) 4123 { 4124 ImGuiContext& g = *context; 4125 IM_ASSERT(!g.Initialized && !g.SettingsLoaded); 4126 4127 // Add .ini handle for ImGuiWindow type 4128 { 4129 ImGuiSettingsHandler ini_handler; 4130 ini_handler.TypeName = "Window"; 4131 ini_handler.TypeHash = ImHashStr("Window"); 4132 ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; 4133 ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; 4134 ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; 4135 ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; 4136 ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; 4137 g.SettingsHandlers.push_back(ini_handler); 4138 } 4139 4140 // Add .ini handle for ImGuiTable type 4141 TableSettingsInstallHandler(context); 4142 4143 // Create default viewport 4144 ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); 4145 g.Viewports.push_back(viewport); 4146 4147 #ifdef IMGUI_HAS_DOCK 4148 #endif // #ifdef IMGUI_HAS_DOCK 4149 4150 g.Initialized = true; 4151 } 4152 4153 // This function is merely here to free heap allocations. 4154 void ImGui::Shutdown(ImGuiContext* context) 4155 { 4156 // 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) 4157 ImGuiContext& g = *context; 4158 if (g.IO.Fonts && g.FontAtlasOwnedByContext) 4159 { 4160 g.IO.Fonts->Locked = false; 4161 IM_DELETE(g.IO.Fonts); 4162 } 4163 g.IO.Fonts = NULL; 4164 4165 // Cleanup of other data are conditional on actually having initialized Dear ImGui. 4166 if (!g.Initialized) 4167 return; 4168 4169 // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) 4170 if (g.SettingsLoaded && g.IO.IniFilename != NULL) 4171 { 4172 ImGuiContext* backup_context = GImGui; 4173 SetCurrentContext(&g); 4174 SaveIniSettingsToDisk(g.IO.IniFilename); 4175 SetCurrentContext(backup_context); 4176 } 4177 4178 CallContextHooks(&g, ImGuiContextHookType_Shutdown); 4179 4180 // Clear everything else 4181 for (int i = 0; i < g.Windows.Size; i++) 4182 IM_DELETE(g.Windows[i]); 4183 g.Windows.clear(); 4184 g.WindowsFocusOrder.clear(); 4185 g.WindowsTempSortBuffer.clear(); 4186 g.CurrentWindow = NULL; 4187 g.CurrentWindowStack.clear(); 4188 g.WindowsById.Clear(); 4189 g.NavWindow = NULL; 4190 g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; 4191 g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; 4192 g.MovingWindow = NULL; 4193 g.ColorStack.clear(); 4194 g.StyleVarStack.clear(); 4195 g.FontStack.clear(); 4196 g.OpenPopupStack.clear(); 4197 g.BeginPopupStack.clear(); 4198 4199 for (int i = 0; i < g.Viewports.Size; i++) 4200 IM_DELETE(g.Viewports[i]); 4201 g.Viewports.clear(); 4202 4203 g.TabBars.Clear(); 4204 g.CurrentTabBarStack.clear(); 4205 g.ShrinkWidthBuffer.clear(); 4206 4207 g.Tables.Clear(); 4208 for (int i = 0; i < g.TablesTempDataStack.Size; i++) 4209 g.TablesTempDataStack[i].~ImGuiTableTempData(); 4210 g.TablesTempDataStack.clear(); 4211 g.DrawChannelsTempMergeBuffer.clear(); 4212 4213 g.ClipboardHandlerData.clear(); 4214 g.MenusIdSubmittedThisFrame.clear(); 4215 g.InputTextState.ClearFreeMemory(); 4216 4217 g.SettingsWindows.clear(); 4218 g.SettingsHandlers.clear(); 4219 4220 if (g.LogFile) 4221 { 4222 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 4223 if (g.LogFile != stdout) 4224 #endif 4225 ImFileClose(g.LogFile); 4226 g.LogFile = NULL; 4227 } 4228 g.LogBuffer.clear(); 4229 4230 g.Initialized = false; 4231 } 4232 4233 // FIXME: Add a more explicit sort order in the window structure. 4234 static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) 4235 { 4236 const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; 4237 const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; 4238 if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) 4239 return d; 4240 if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) 4241 return d; 4242 return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); 4243 } 4244 4245 static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window) 4246 { 4247 out_sorted_windows->push_back(window); 4248 if (window->Active) 4249 { 4250 int count = window->DC.ChildWindows.Size; 4251 if (count > 1) 4252 ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); 4253 for (int i = 0; i < count; i++) 4254 { 4255 ImGuiWindow* child = window->DC.ChildWindows[i]; 4256 if (child->Active) 4257 AddWindowToSortBuffer(out_sorted_windows, child); 4258 } 4259 } 4260 } 4261 4262 static void AddDrawListToDrawData(ImVector<ImDrawList*>* out_list, ImDrawList* draw_list) 4263 { 4264 // Remove trailing command if unused. 4265 // Technically we could return directly instead of popping, but this make things looks neat in Metrics/Debugger window as well. 4266 draw_list->_PopUnusedDrawCmd(); 4267 if (draw_list->CmdBuffer.Size == 0) 4268 return; 4269 4270 // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. 4271 // May trigger for you if you are using PrimXXX functions incorrectly. 4272 IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); 4273 IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); 4274 if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) 4275 IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); 4276 4277 // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) 4278 // If this assert triggers because you are drawing lots of stuff manually: 4279 // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. 4280 // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. 4281 // - If you want large meshes with more than 64K vertices, you can either: 4282 // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. 4283 // Most example backends already support this from 1.71. Pre-1.71 backends won't. 4284 // 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. 4285 // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. 4286 // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: 4287 // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); 4288 // Your own engine or render API may use different parameters or function calls to specify index sizes. 4289 // 2 and 4 bytes indices are generally supported by most graphics API. 4290 // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching 4291 // the 64K limit to split your draw commands in multiple draw lists. 4292 if (sizeof(ImDrawIdx) == 2) 4293 IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); 4294 4295 out_list->push_back(draw_list); 4296 } 4297 4298 static void AddWindowToDrawData(ImGuiWindow* window, int layer) 4299 { 4300 ImGuiContext& g = *GImGui; 4301 ImGuiViewportP* viewport = g.Viewports[0]; 4302 g.IO.MetricsRenderWindows++; 4303 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); 4304 for (int i = 0; i < window->DC.ChildWindows.Size; i++) 4305 { 4306 ImGuiWindow* child = window->DC.ChildWindows[i]; 4307 if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active 4308 AddWindowToDrawData(child, layer); 4309 } 4310 } 4311 4312 // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) 4313 static void AddRootWindowToDrawData(ImGuiWindow* window) 4314 { 4315 int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; 4316 AddWindowToDrawData(window, layer); 4317 } 4318 4319 void ImDrawDataBuilder::FlattenIntoSingleLayer() 4320 { 4321 int n = Layers[0].Size; 4322 int size = n; 4323 for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) 4324 size += Layers[i].Size; 4325 Layers[0].resize(size); 4326 for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) 4327 { 4328 ImVector<ImDrawList*>& layer = Layers[layer_n]; 4329 if (layer.empty()) 4330 continue; 4331 memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); 4332 n += layer.Size; 4333 layer.resize(0); 4334 } 4335 } 4336 4337 static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector<ImDrawList*>* draw_lists) 4338 { 4339 ImGuiIO& io = ImGui::GetIO(); 4340 ImDrawData* draw_data = &viewport->DrawDataP; 4341 draw_data->Valid = true; 4342 draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; 4343 draw_data->CmdListsCount = draw_lists->Size; 4344 draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; 4345 draw_data->DisplayPos = viewport->Pos; 4346 draw_data->DisplaySize = viewport->Size; 4347 draw_data->FramebufferScale = io.DisplayFramebufferScale; 4348 for (int n = 0; n < draw_lists->Size; n++) 4349 { 4350 draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; 4351 draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; 4352 } 4353 } 4354 4355 // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. 4356 // - When using this function it is sane to ensure that float are perfectly rounded to integer values, 4357 // so that e.g. (int)(max.x-min.x) in user's render produce correct result. 4358 // - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect(): 4359 // some frequently called functions which to modify both channels and clipping simultaneously tend to use the 4360 // more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds. 4361 void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) 4362 { 4363 ImGuiWindow* window = GetCurrentWindow(); 4364 window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); 4365 window->ClipRect = window->DrawList->_ClipRectStack.back(); 4366 } 4367 4368 void ImGui::PopClipRect() 4369 { 4370 ImGuiWindow* window = GetCurrentWindow(); 4371 window->DrawList->PopClipRect(); 4372 window->ClipRect = window->DrawList->_ClipRectStack.back(); 4373 } 4374 4375 // 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. 4376 void ImGui::EndFrame() 4377 { 4378 ImGuiContext& g = *GImGui; 4379 IM_ASSERT(g.Initialized); 4380 4381 // Don't process EndFrame() multiple times. 4382 if (g.FrameCountEnded == g.FrameCount) 4383 return; 4384 IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?"); 4385 4386 CallContextHooks(&g, ImGuiContextHookType_EndFramePre); 4387 4388 ErrorCheckEndFrameSanityChecks(); 4389 4390 // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) 4391 if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) 4392 { 4393 g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); 4394 g.PlatformImeLastPos = g.PlatformImePos; 4395 } 4396 4397 // Hide implicit/fallback "Debug" window if it hasn't been used 4398 g.WithinFrameScopeWithImplicitWindow = false; 4399 if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) 4400 g.CurrentWindow->Active = false; 4401 End(); 4402 4403 // Update navigation: CTRL+Tab, wrap-around requests 4404 NavEndFrame(); 4405 4406 // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) 4407 if (g.DragDropActive) 4408 { 4409 bool is_delivered = g.DragDropPayload.Delivery; 4410 bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton)); 4411 if (is_delivered || is_elapsed) 4412 ClearDragDrop(); 4413 } 4414 4415 // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing. 4416 if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 4417 { 4418 g.DragDropWithinSource = true; 4419 SetTooltip("..."); 4420 g.DragDropWithinSource = false; 4421 } 4422 4423 // End frame 4424 g.WithinFrameScope = false; 4425 g.FrameCountEnded = g.FrameCount; 4426 4427 // Initiate moving window + handle left-click and right-click focus 4428 UpdateMouseMovingWindowEndFrame(); 4429 4430 // Sort the window list so that all child windows are after their parent 4431 // We cannot do that on FocusWindow() because children may not exist yet 4432 g.WindowsTempSortBuffer.resize(0); 4433 g.WindowsTempSortBuffer.reserve(g.Windows.Size); 4434 for (int i = 0; i != g.Windows.Size; i++) 4435 { 4436 ImGuiWindow* window = g.Windows[i]; 4437 if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it 4438 continue; 4439 AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window); 4440 } 4441 4442 // 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. 4443 IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size); 4444 g.Windows.swap(g.WindowsTempSortBuffer); 4445 g.IO.MetricsActiveWindows = g.WindowsActiveCount; 4446 4447 // Unlock font atlas 4448 g.IO.Fonts->Locked = false; 4449 4450 // Clear Input data for next frame 4451 g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; 4452 g.IO.InputQueueCharacters.resize(0); 4453 memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); 4454 4455 CallContextHooks(&g, ImGuiContextHookType_EndFramePost); 4456 } 4457 4458 void ImGui::Render() 4459 { 4460 ImGuiContext& g = *GImGui; 4461 IM_ASSERT(g.Initialized); 4462 4463 if (g.FrameCountEnded != g.FrameCount) 4464 EndFrame(); 4465 g.FrameCountRendered = g.FrameCount; 4466 g.IO.MetricsRenderWindows = 0; 4467 4468 CallContextHooks(&g, ImGuiContextHookType_RenderPre); 4469 4470 // Add background ImDrawList (for each active viewport) 4471 for (int n = 0; n != g.Viewports.Size; n++) 4472 { 4473 ImGuiViewportP* viewport = g.Viewports[n]; 4474 viewport->DrawDataBuilder.Clear(); 4475 if (viewport->DrawLists[0] != NULL) 4476 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); 4477 } 4478 4479 // Add ImDrawList to render 4480 ImGuiWindow* windows_to_render_top_most[2]; 4481 windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; 4482 windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); 4483 for (int n = 0; n != g.Windows.Size; n++) 4484 { 4485 ImGuiWindow* window = g.Windows[n]; 4486 IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" 4487 if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) 4488 AddRootWindowToDrawData(window); 4489 } 4490 for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) 4491 if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window 4492 AddRootWindowToDrawData(windows_to_render_top_most[n]); 4493 4494 // Setup ImDrawData structures for end-user 4495 g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; 4496 for (int n = 0; n < g.Viewports.Size; n++) 4497 { 4498 ImGuiViewportP* viewport = g.Viewports[n]; 4499 viewport->DrawDataBuilder.FlattenIntoSingleLayer(); 4500 4501 // Draw software mouse cursor if requested by io.MouseDrawCursor flag 4502 if (g.IO.MouseDrawCursor) 4503 RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); 4504 4505 // Add foreground ImDrawList (for each active viewport) 4506 if (viewport->DrawLists[1] != NULL) 4507 AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); 4508 4509 SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); 4510 ImDrawData* draw_data = &viewport->DrawDataP; 4511 g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; 4512 g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; 4513 } 4514 4515 CallContextHooks(&g, ImGuiContextHookType_RenderPost); 4516 } 4517 4518 // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. 4519 // CalcTextSize("") should return ImVec2(0.0f, g.FontSize) 4520 ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) 4521 { 4522 ImGuiContext& g = *GImGui; 4523 4524 const char* text_display_end; 4525 if (hide_text_after_double_hash) 4526 text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string 4527 else 4528 text_display_end = text_end; 4529 4530 ImFont* font = g.Font; 4531 const float font_size = g.FontSize; 4532 if (text == text_display_end) 4533 return ImVec2(0.0f, font_size); 4534 ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); 4535 4536 // Round 4537 // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out. 4538 // FIXME: Investigate using ceilf or e.g. 4539 // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c 4540 // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html 4541 text_size.x = IM_FLOOR(text_size.x + 0.99999f); 4542 4543 return text_size; 4544 } 4545 4546 // Find window given position, search front-to-back 4547 // FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically 4548 // with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is 4549 // called, aka before the next Begin(). Moving window isn't affected. 4550 static void FindHoveredWindow() 4551 { 4552 ImGuiContext& g = *GImGui; 4553 4554 ImGuiWindow* hovered_window = NULL; 4555 ImGuiWindow* hovered_window_ignoring_moving_window = NULL; 4556 if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) 4557 hovered_window = g.MovingWindow; 4558 4559 ImVec2 padding_regular = g.Style.TouchExtraPadding; 4560 ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular; 4561 for (int i = g.Windows.Size - 1; i >= 0; i--) 4562 { 4563 ImGuiWindow* window = g.Windows[i]; 4564 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. 4565 if (!window->Active || window->Hidden) 4566 continue; 4567 if (window->Flags & ImGuiWindowFlags_NoMouseInputs) 4568 continue; 4569 4570 // Using the clipped AABB, a child window will typically be clipped by its parent (not always) 4571 ImRect bb(window->OuterRectClipped); 4572 if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) 4573 bb.Expand(padding_regular); 4574 else 4575 bb.Expand(padding_for_resize); 4576 if (!bb.Contains(g.IO.MousePos)) 4577 continue; 4578 4579 // Support for one rectangular hole in any given window 4580 // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) 4581 if (window->HitTestHoleSize.x != 0) 4582 { 4583 ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); 4584 ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); 4585 if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos)) 4586 continue; 4587 } 4588 4589 if (hovered_window == NULL) 4590 hovered_window = window; 4591 IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. 4592 if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) 4593 hovered_window_ignoring_moving_window = window; 4594 if (hovered_window && hovered_window_ignoring_moving_window) 4595 break; 4596 } 4597 4598 g.HoveredWindow = hovered_window; 4599 g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; 4600 } 4601 4602 // Test if mouse cursor is hovering given rectangle 4603 // NB- Rectangle is clipped by our current clip setting 4604 // NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) 4605 bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) 4606 { 4607 ImGuiContext& g = *GImGui; 4608 4609 // Clip 4610 ImRect rect_clipped(r_min, r_max); 4611 if (clip) 4612 rect_clipped.ClipWith(g.CurrentWindow->ClipRect); 4613 4614 // Expand for touch input 4615 const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); 4616 if (!rect_for_touch.Contains(g.IO.MousePos)) 4617 return false; 4618 return true; 4619 } 4620 4621 int ImGui::GetKeyIndex(ImGuiKey imgui_key) 4622 { 4623 IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); 4624 ImGuiContext& g = *GImGui; 4625 return g.IO.KeyMap[imgui_key]; 4626 } 4627 4628 // Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]! 4629 // Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! 4630 bool ImGui::IsKeyDown(int user_key_index) 4631 { 4632 if (user_key_index < 0) 4633 return false; 4634 ImGuiContext& g = *GImGui; 4635 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4636 return g.IO.KeysDown[user_key_index]; 4637 } 4638 4639 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) 4640 // t1 = current time (e.g.: g.Time) 4641 // An event is triggered at: 4642 // t = 0.0f t = repeat_delay, t = repeat_delay + repeat_rate*N 4643 int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate) 4644 { 4645 if (t1 == 0.0f) 4646 return 1; 4647 if (t0 >= t1) 4648 return 0; 4649 if (repeat_rate <= 0.0f) 4650 return (t0 < repeat_delay) && (t1 >= repeat_delay); 4651 const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate); 4652 const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate); 4653 const int count = count_t1 - count_t0; 4654 return count; 4655 } 4656 4657 int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) 4658 { 4659 ImGuiContext& g = *GImGui; 4660 if (key_index < 0) 4661 return 0; 4662 IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4663 const float t = g.IO.KeysDownDuration[key_index]; 4664 return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); 4665 } 4666 4667 bool ImGui::IsKeyPressed(int user_key_index, bool repeat) 4668 { 4669 ImGuiContext& g = *GImGui; 4670 if (user_key_index < 0) 4671 return false; 4672 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4673 const float t = g.IO.KeysDownDuration[user_key_index]; 4674 if (t == 0.0f) 4675 return true; 4676 if (repeat && t > g.IO.KeyRepeatDelay) 4677 return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; 4678 return false; 4679 } 4680 4681 bool ImGui::IsKeyReleased(int user_key_index) 4682 { 4683 ImGuiContext& g = *GImGui; 4684 if (user_key_index < 0) return false; 4685 IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); 4686 return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; 4687 } 4688 4689 bool ImGui::IsMouseDown(ImGuiMouseButton button) 4690 { 4691 ImGuiContext& g = *GImGui; 4692 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4693 return g.IO.MouseDown[button]; 4694 } 4695 4696 bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) 4697 { 4698 ImGuiContext& g = *GImGui; 4699 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4700 const float t = g.IO.MouseDownDuration[button]; 4701 if (t == 0.0f) 4702 return true; 4703 4704 if (repeat && t > g.IO.KeyRepeatDelay) 4705 { 4706 // 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. 4707 int amount = CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate * 0.50f); 4708 if (amount > 0) 4709 return true; 4710 } 4711 return false; 4712 } 4713 4714 bool ImGui::IsMouseReleased(ImGuiMouseButton button) 4715 { 4716 ImGuiContext& g = *GImGui; 4717 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4718 return g.IO.MouseReleased[button]; 4719 } 4720 4721 bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) 4722 { 4723 ImGuiContext& g = *GImGui; 4724 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4725 return g.IO.MouseDoubleClicked[button]; 4726 } 4727 4728 // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. 4729 // [Internal] This doesn't test if the button is pressed 4730 bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) 4731 { 4732 ImGuiContext& g = *GImGui; 4733 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4734 if (lock_threshold < 0.0f) 4735 lock_threshold = g.IO.MouseDragThreshold; 4736 return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; 4737 } 4738 4739 bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold) 4740 { 4741 ImGuiContext& g = *GImGui; 4742 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4743 if (!g.IO.MouseDown[button]) 4744 return false; 4745 return IsMouseDragPastThreshold(button, lock_threshold); 4746 } 4747 4748 ImVec2 ImGui::GetMousePos() 4749 { 4750 ImGuiContext& g = *GImGui; 4751 return g.IO.MousePos; 4752 } 4753 4754 // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! 4755 ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() 4756 { 4757 ImGuiContext& g = *GImGui; 4758 if (g.BeginPopupStack.Size > 0) 4759 return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos; 4760 return g.IO.MousePos; 4761 } 4762 4763 // We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. 4764 bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) 4765 { 4766 // The assert is only to silence a false-positive in XCode Static Analysis. 4767 // 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). 4768 IM_ASSERT(GImGui != NULL); 4769 const float MOUSE_INVALID = -256000.0f; 4770 ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; 4771 return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; 4772 } 4773 4774 bool ImGui::IsAnyMouseDown() 4775 { 4776 ImGuiContext& g = *GImGui; 4777 for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) 4778 if (g.IO.MouseDown[n]) 4779 return true; 4780 return false; 4781 } 4782 4783 // Return the delta from the initial clicking position while the mouse button is clicked or was just released. 4784 // This is locked and return 0.0f until the mouse moves past a distance threshold at least once. 4785 // NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window. 4786 ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold) 4787 { 4788 ImGuiContext& g = *GImGui; 4789 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4790 if (lock_threshold < 0.0f) 4791 lock_threshold = g.IO.MouseDragThreshold; 4792 if (g.IO.MouseDown[button] || g.IO.MouseReleased[button]) 4793 if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) 4794 if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button])) 4795 return g.IO.MousePos - g.IO.MouseClickedPos[button]; 4796 return ImVec2(0.0f, 0.0f); 4797 } 4798 4799 void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) 4800 { 4801 ImGuiContext& g = *GImGui; 4802 IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); 4803 // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr 4804 g.IO.MouseClickedPos[button] = g.IO.MousePos; 4805 } 4806 4807 ImGuiMouseCursor ImGui::GetMouseCursor() 4808 { 4809 return GImGui->MouseCursor; 4810 } 4811 4812 void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) 4813 { 4814 GImGui->MouseCursor = cursor_type; 4815 } 4816 4817 void ImGui::CaptureKeyboardFromApp(bool capture) 4818 { 4819 GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0; 4820 } 4821 4822 void ImGui::CaptureMouseFromApp(bool capture) 4823 { 4824 GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0; 4825 } 4826 4827 bool ImGui::IsItemActive() 4828 { 4829 ImGuiContext& g = *GImGui; 4830 if (g.ActiveId) 4831 { 4832 ImGuiWindow* window = g.CurrentWindow; 4833 return g.ActiveId == window->DC.LastItemId; 4834 } 4835 return false; 4836 } 4837 4838 bool ImGui::IsItemActivated() 4839 { 4840 ImGuiContext& g = *GImGui; 4841 if (g.ActiveId) 4842 { 4843 ImGuiWindow* window = g.CurrentWindow; 4844 if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) 4845 return true; 4846 } 4847 return false; 4848 } 4849 4850 bool ImGui::IsItemDeactivated() 4851 { 4852 ImGuiContext& g = *GImGui; 4853 ImGuiWindow* window = g.CurrentWindow; 4854 if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) 4855 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; 4856 return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); 4857 } 4858 4859 bool ImGui::IsItemDeactivatedAfterEdit() 4860 { 4861 ImGuiContext& g = *GImGui; 4862 return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore)); 4863 } 4864 4865 // == GetItemID() == GetFocusID() 4866 bool ImGui::IsItemFocused() 4867 { 4868 ImGuiContext& g = *GImGui; 4869 ImGuiWindow* window = g.CurrentWindow; 4870 4871 if (g.NavId != window->DC.LastItemId || g.NavId == 0) 4872 return false; 4873 return true; 4874 } 4875 4876 // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()! 4877 // Most widgets have specific reactions based on mouse-up/down state, mouse position etc. 4878 bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) 4879 { 4880 return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); 4881 } 4882 4883 bool ImGui::IsItemToggledOpen() 4884 { 4885 ImGuiContext& g = *GImGui; 4886 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; 4887 } 4888 4889 bool ImGui::IsItemToggledSelection() 4890 { 4891 ImGuiContext& g = *GImGui; 4892 return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; 4893 } 4894 4895 bool ImGui::IsAnyItemHovered() 4896 { 4897 ImGuiContext& g = *GImGui; 4898 return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; 4899 } 4900 4901 bool ImGui::IsAnyItemActive() 4902 { 4903 ImGuiContext& g = *GImGui; 4904 return g.ActiveId != 0; 4905 } 4906 4907 bool ImGui::IsAnyItemFocused() 4908 { 4909 ImGuiContext& g = *GImGui; 4910 return g.NavId != 0 && !g.NavDisableHighlight; 4911 } 4912 4913 bool ImGui::IsItemVisible() 4914 { 4915 ImGuiWindow* window = GetCurrentWindowRead(); 4916 return window->ClipRect.Overlaps(window->DC.LastItemRect); 4917 } 4918 4919 bool ImGui::IsItemEdited() 4920 { 4921 ImGuiWindow* window = GetCurrentWindowRead(); 4922 return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; 4923 } 4924 4925 // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. 4926 // FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. 4927 void ImGui::SetItemAllowOverlap() 4928 { 4929 ImGuiContext& g = *GImGui; 4930 ImGuiID id = g.CurrentWindow->DC.LastItemId; 4931 if (g.HoveredId == id) 4932 g.HoveredIdAllowOverlap = true; 4933 if (g.ActiveId == id) 4934 g.ActiveIdAllowOverlap = true; 4935 } 4936 4937 void ImGui::SetItemUsingMouseWheel() 4938 { 4939 ImGuiContext& g = *GImGui; 4940 ImGuiID id = g.CurrentWindow->DC.LastItemId; 4941 if (g.HoveredId == id) 4942 g.HoveredIdUsingMouseWheel = true; 4943 if (g.ActiveId == id) 4944 g.ActiveIdUsingMouseWheel = true; 4945 } 4946 4947 ImVec2 ImGui::GetItemRectMin() 4948 { 4949 ImGuiWindow* window = GetCurrentWindowRead(); 4950 return window->DC.LastItemRect.Min; 4951 } 4952 4953 ImVec2 ImGui::GetItemRectMax() 4954 { 4955 ImGuiWindow* window = GetCurrentWindowRead(); 4956 return window->DC.LastItemRect.Max; 4957 } 4958 4959 ImVec2 ImGui::GetItemRectSize() 4960 { 4961 ImGuiWindow* window = GetCurrentWindowRead(); 4962 return window->DC.LastItemRect.GetSize(); 4963 } 4964 4965 bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) 4966 { 4967 ImGuiContext& g = *GImGui; 4968 ImGuiWindow* parent_window = g.CurrentWindow; 4969 4970 flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow; 4971 flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag 4972 4973 // Size 4974 const ImVec2 content_avail = GetContentRegionAvail(); 4975 ImVec2 size = ImFloor(size_arg); 4976 const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); 4977 if (size.x <= 0.0f) 4978 size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) 4979 if (size.y <= 0.0f) 4980 size.y = ImMax(content_avail.y + size.y, 4.0f); 4981 SetNextWindowSize(size); 4982 4983 // 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. 4984 if (name) 4985 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%s_%08X", parent_window->Name, name, id); 4986 else 4987 ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%s/%08X", parent_window->Name, id); 4988 4989 const float backup_border_size = g.Style.ChildBorderSize; 4990 if (!border) 4991 g.Style.ChildBorderSize = 0.0f; 4992 bool ret = Begin(g.TempBuffer, NULL, flags); 4993 g.Style.ChildBorderSize = backup_border_size; 4994 4995 ImGuiWindow* child_window = g.CurrentWindow; 4996 child_window->ChildId = id; 4997 child_window->AutoFitChildAxises = (ImS8)auto_fit_axises; 4998 4999 // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. 5000 // While this is not really documented/defined, it seems that the expected thing to do. 5001 if (child_window->BeginCount == 1) 5002 parent_window->DC.CursorPos = child_window->Pos; 5003 5004 // Process navigation-in immediately so NavInit can run on first frame 5005 if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) 5006 { 5007 FocusWindow(child_window); 5008 NavInitWindow(child_window, false); 5009 SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item 5010 g.ActiveIdSource = ImGuiInputSource_Nav; 5011 } 5012 return ret; 5013 } 5014 5015 bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 5016 { 5017 ImGuiWindow* window = GetCurrentWindow(); 5018 return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); 5019 } 5020 5021 bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) 5022 { 5023 IM_ASSERT(id != 0); 5024 return BeginChildEx(NULL, id, size_arg, border, extra_flags); 5025 } 5026 5027 void ImGui::EndChild() 5028 { 5029 ImGuiContext& g = *GImGui; 5030 ImGuiWindow* window = g.CurrentWindow; 5031 5032 IM_ASSERT(g.WithinEndChild == false); 5033 IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls 5034 5035 g.WithinEndChild = true; 5036 if (window->BeginCount > 1) 5037 { 5038 End(); 5039 } 5040 else 5041 { 5042 ImVec2 sz = window->Size; 5043 if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f 5044 sz.x = ImMax(4.0f, sz.x); 5045 if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) 5046 sz.y = ImMax(4.0f, sz.y); 5047 End(); 5048 5049 ImGuiWindow* parent_window = g.CurrentWindow; 5050 ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); 5051 ItemSize(sz); 5052 if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) 5053 { 5054 ItemAdd(bb, window->ChildId); 5055 RenderNavHighlight(bb, window->ChildId); 5056 5057 // When browsing a window that has no activable items (scroll only) we keep a highlight on the child 5058 if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) 5059 RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); 5060 } 5061 else 5062 { 5063 // Not navigable into 5064 ItemAdd(bb, 0); 5065 } 5066 if (g.HoveredWindow == window) 5067 parent_window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; 5068 } 5069 g.WithinEndChild = false; 5070 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return 5071 } 5072 5073 // Helper to create a child window / scrolling region that looks like a normal widget frame. 5074 bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) 5075 { 5076 ImGuiContext& g = *GImGui; 5077 const ImGuiStyle& style = g.Style; 5078 PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); 5079 PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); 5080 PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); 5081 PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); 5082 bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); 5083 PopStyleVar(3); 5084 PopStyleColor(); 5085 return ret; 5086 } 5087 5088 void ImGui::EndChildFrame() 5089 { 5090 EndChild(); 5091 } 5092 5093 static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) 5094 { 5095 window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); 5096 window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); 5097 window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); 5098 } 5099 5100 ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) 5101 { 5102 ImGuiContext& g = *GImGui; 5103 return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); 5104 } 5105 5106 ImGuiWindow* ImGui::FindWindowByName(const char* name) 5107 { 5108 ImGuiID id = ImHashStr(name); 5109 return FindWindowByID(id); 5110 } 5111 5112 static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) 5113 { 5114 window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y)); 5115 if (settings->Size.x > 0 && settings->Size.y > 0) 5116 window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y)); 5117 window->Collapsed = settings->Collapsed; 5118 } 5119 5120 static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) 5121 { 5122 ImGuiContext& g = *GImGui; 5123 //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); 5124 5125 // Create window the first time 5126 ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); 5127 window->Flags = flags; 5128 g.WindowsById.SetVoidPtr(window->ID, window); 5129 5130 // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. 5131 const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); 5132 window->Pos = main_viewport->Pos + ImVec2(60, 60); 5133 5134 // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. 5135 if (!(flags & ImGuiWindowFlags_NoSavedSettings)) 5136 if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) 5137 { 5138 // Retrieve settings from .ini file 5139 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); 5140 SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); 5141 ApplyWindowSettings(window, settings); 5142 } 5143 window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values 5144 5145 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) 5146 { 5147 window->AutoFitFramesX = window->AutoFitFramesY = 2; 5148 window->AutoFitOnlyGrows = false; 5149 } 5150 else 5151 { 5152 if (window->Size.x <= 0.0f) 5153 window->AutoFitFramesX = 2; 5154 if (window->Size.y <= 0.0f) 5155 window->AutoFitFramesY = 2; 5156 window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); 5157 } 5158 5159 if (!(flags & ImGuiWindowFlags_ChildWindow)) 5160 { 5161 g.WindowsFocusOrder.push_back(window); 5162 window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); 5163 } 5164 5165 if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) 5166 g.Windows.push_front(window); // Quite slow but rare and only once 5167 else 5168 g.Windows.push_back(window); 5169 return window; 5170 } 5171 5172 static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired) 5173 { 5174 ImGuiContext& g = *GImGui; 5175 ImVec2 new_size = size_desired; 5176 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) 5177 { 5178 // Using -1,-1 on either X/Y axis to preserve the current size. 5179 ImRect cr = g.NextWindowData.SizeConstraintRect; 5180 new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; 5181 new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; 5182 if (g.NextWindowData.SizeCallback) 5183 { 5184 ImGuiSizeCallbackData data; 5185 data.UserData = g.NextWindowData.SizeCallbackUserData; 5186 data.Pos = window->Pos; 5187 data.CurrentSize = window->SizeFull; 5188 data.DesiredSize = new_size; 5189 g.NextWindowData.SizeCallback(&data); 5190 new_size = data.DesiredSize; 5191 } 5192 new_size.x = IM_FLOOR(new_size.x); 5193 new_size.y = IM_FLOOR(new_size.y); 5194 } 5195 5196 // Minimum size 5197 if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) 5198 { 5199 ImGuiWindow* window_for_height = window; 5200 const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight(); 5201 new_size = ImMax(new_size, g.Style.WindowMinSize); 5202 new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows 5203 } 5204 return new_size; 5205 } 5206 5207 static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal) 5208 { 5209 bool preserve_old_content_sizes = false; 5210 if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 5211 preserve_old_content_sizes = true; 5212 else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0) 5213 preserve_old_content_sizes = true; 5214 if (preserve_old_content_sizes) 5215 { 5216 *content_size_current = window->ContentSize; 5217 *content_size_ideal = window->ContentSizeIdeal; 5218 return; 5219 } 5220 5221 content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); 5222 content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); 5223 content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); 5224 content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); 5225 } 5226 5227 static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) 5228 { 5229 ImGuiContext& g = *GImGui; 5230 ImGuiStyle& style = g.Style; 5231 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 5232 ImVec2 size_pad = window->WindowPadding * 2.0f; 5233 ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); 5234 if (window->Flags & ImGuiWindowFlags_Tooltip) 5235 { 5236 // Tooltip always resize 5237 return size_desired; 5238 } 5239 else 5240 { 5241 // Maximum window size is determined by the viewport size or monitor size 5242 const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; 5243 const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; 5244 ImVec2 size_min = style.WindowMinSize; 5245 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) 5246 size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); 5247 5248 // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows? 5249 ImVec2 avail_size = ImGui::GetMainViewport()->Size; 5250 ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f)); 5251 5252 // When the window cannot fit all contents (either because of constraints, either because screen is too small), 5253 // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. 5254 ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5255 bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); 5256 bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); 5257 if (will_have_scrollbar_x) 5258 size_auto_fit.y += style.ScrollbarSize; 5259 if (will_have_scrollbar_y) 5260 size_auto_fit.x += style.ScrollbarSize; 5261 return size_auto_fit; 5262 } 5263 } 5264 5265 ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) 5266 { 5267 ImVec2 size_contents_current; 5268 ImVec2 size_contents_ideal; 5269 CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal); 5270 ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal); 5271 ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5272 return size_final; 5273 } 5274 5275 static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) 5276 { 5277 if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) 5278 return ImGuiCol_PopupBg; 5279 if (flags & ImGuiWindowFlags_ChildWindow) 5280 return ImGuiCol_ChildBg; 5281 return ImGuiCol_WindowBg; 5282 } 5283 5284 static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) 5285 { 5286 ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left 5287 ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right 5288 ImVec2 size_expected = pos_max - pos_min; 5289 ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected); 5290 *out_pos = pos_min; 5291 if (corner_norm.x == 0.0f) 5292 out_pos->x -= (size_constrained.x - size_expected.x); 5293 if (corner_norm.y == 0.0f) 5294 out_pos->y -= (size_constrained.y - size_expected.y); 5295 *out_size = size_constrained; 5296 } 5297 5298 // Data for resizing from corner 5299 struct ImGuiResizeGripDef 5300 { 5301 ImVec2 CornerPosN; 5302 ImVec2 InnerDir; 5303 int AngleMin12, AngleMax12; 5304 }; 5305 static const ImGuiResizeGripDef resize_grip_def[4] = 5306 { 5307 { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right 5308 { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left 5309 { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused) 5310 { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused) 5311 }; 5312 5313 // Data for resizing from borders 5314 struct ImGuiResizeBorderDef 5315 { 5316 ImVec2 InnerDir; 5317 ImVec2 SegmentN1, SegmentN2; 5318 float OuterAngle; 5319 }; 5320 static const ImGuiResizeBorderDef resize_border_def[4] = 5321 { 5322 { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left 5323 { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right 5324 { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up 5325 { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f } // Down 5326 }; 5327 5328 static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) 5329 { 5330 ImRect rect = window->Rect(); 5331 if (thickness == 0.0f) 5332 rect.Max -= ImVec2(1, 1); 5333 if (border_n == ImGuiDir_Left) { return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); } 5334 if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); } 5335 if (border_n == ImGuiDir_Up) { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); } 5336 if (border_n == ImGuiDir_Down) { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); } 5337 IM_ASSERT(0); 5338 return ImRect(); 5339 } 5340 5341 // 0..3: corners (Lower-right, Lower-left, Unused, Unused) 5342 ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n) 5343 { 5344 IM_ASSERT(n >= 0 && n < 4); 5345 ImGuiID id = window->ID; 5346 id = ImHashStr("#RESIZE", 0, id); 5347 id = ImHashData(&n, sizeof(int), id); 5348 return id; 5349 } 5350 5351 // Borders (Left, Right, Up, Down) 5352 ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) 5353 { 5354 IM_ASSERT(dir >= 0 && dir < 4); 5355 int n = (int)dir + 4; 5356 ImGuiID id = window->ID; 5357 id = ImHashStr("#RESIZE", 0, id); 5358 id = ImHashData(&n, sizeof(int), id); 5359 return id; 5360 } 5361 5362 // Handle resize for: Resize Grips, Borders, Gamepad 5363 // Return true when using auto-fit (double click on resize grip) 5364 static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) 5365 { 5366 ImGuiContext& g = *GImGui; 5367 ImGuiWindowFlags flags = window->Flags; 5368 5369 if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5370 return false; 5371 if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. 5372 return false; 5373 5374 bool ret_auto_fit = false; 5375 const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; 5376 const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); 5377 const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); 5378 const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f; 5379 5380 ImVec2 pos_target(FLT_MAX, FLT_MAX); 5381 ImVec2 size_target(FLT_MAX, FLT_MAX); 5382 5383 // Resize grips and borders are on layer 1 5384 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; 5385 5386 // Manual resize grips 5387 PushID("#RESIZE"); 5388 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5389 { 5390 const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n]; 5391 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN); 5392 5393 // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window 5394 bool hovered, held; 5395 ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size); 5396 if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); 5397 if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); 5398 ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID() 5399 ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); 5400 //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); 5401 if (hovered || held) 5402 g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; 5403 5404 if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) 5405 { 5406 // Manual auto-fit when double-clicking 5407 size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); 5408 ret_auto_fit = true; 5409 ClearActiveID(); 5410 } 5411 else if (held) 5412 { 5413 // Resize from any of the four corners 5414 // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position 5415 ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); 5416 ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); 5417 ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip 5418 corner_target = ImClamp(corner_target, clamp_min, clamp_max); 5419 CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target); 5420 } 5421 5422 // Only lower-left grip is visible before hovering/activating 5423 if (resize_grip_n == 0 || held || hovered) 5424 resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); 5425 } 5426 for (int border_n = 0; border_n < resize_border_count; border_n++) 5427 { 5428 const ImGuiResizeBorderDef& def = resize_border_def[border_n]; 5429 const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; 5430 5431 bool hovered, held; 5432 ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); 5433 ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() 5434 ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren); 5435 //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); 5436 if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) 5437 { 5438 g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; 5439 if (held) 5440 *border_held = border_n; 5441 } 5442 if (held) 5443 { 5444 ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX); 5445 ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX); 5446 ImVec2 border_target = window->Pos; 5447 border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; 5448 border_target = ImClamp(border_target, clamp_min, clamp_max); 5449 CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); 5450 } 5451 } 5452 PopID(); 5453 5454 // Restore nav layer 5455 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5456 5457 // Navigation resize (keyboard/gamepad) 5458 if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) 5459 { 5460 ImVec2 nav_resize_delta; 5461 if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) 5462 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 5463 if (g.NavInputSource == ImGuiInputSource_Gamepad) 5464 nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); 5465 if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) 5466 { 5467 const float NAV_RESIZE_SPEED = 600.0f; 5468 nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); 5469 nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size); 5470 g.NavWindowingToggleLayer = false; 5471 g.NavDisableMouseHover = true; 5472 resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); 5473 // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. 5474 size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); 5475 } 5476 } 5477 5478 // Apply back modified position/size to window 5479 if (size_target.x != FLT_MAX) 5480 { 5481 window->SizeFull = size_target; 5482 MarkIniSettingsDirty(window); 5483 } 5484 if (pos_target.x != FLT_MAX) 5485 { 5486 window->Pos = ImFloor(pos_target); 5487 MarkIniSettingsDirty(window); 5488 } 5489 5490 window->Size = window->SizeFull; 5491 return ret_auto_fit; 5492 } 5493 5494 static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect) 5495 { 5496 ImGuiContext& g = *GImGui; 5497 ImVec2 size_for_clamping = window->Size; 5498 if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5499 size_for_clamping.y = window->TitleBarHeight(); 5500 window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); 5501 } 5502 5503 static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) 5504 { 5505 ImGuiContext& g = *GImGui; 5506 float rounding = window->WindowRounding; 5507 float border_size = window->WindowBorderSize; 5508 if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) 5509 window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); 5510 5511 int border_held = window->ResizeBorderHeld; 5512 if (border_held != -1) 5513 { 5514 const ImGuiResizeBorderDef& def = resize_border_def[border_held]; 5515 ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); 5516 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); 5517 window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); 5518 window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual 5519 } 5520 if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 5521 { 5522 float y = window->Pos.y + window->TitleBarHeight() - 1; 5523 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); 5524 } 5525 } 5526 5527 // Draw background and borders 5528 // Draw and handle scrollbars 5529 void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) 5530 { 5531 ImGuiContext& g = *GImGui; 5532 ImGuiStyle& style = g.Style; 5533 ImGuiWindowFlags flags = window->Flags; 5534 5535 // Ensure that ScrollBar doesn't read last frame's SkipItems 5536 IM_ASSERT(window->BeginCount == 0); 5537 window->SkipItems = false; 5538 5539 // Draw window + handle manual resize 5540 // 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. 5541 const float window_rounding = window->WindowRounding; 5542 const float window_border_size = window->WindowBorderSize; 5543 if (window->Collapsed) 5544 { 5545 // Title bar only 5546 float backup_border_size = style.FrameBorderSize; 5547 g.Style.FrameBorderSize = window->WindowBorderSize; 5548 ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); 5549 RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); 5550 g.Style.FrameBorderSize = backup_border_size; 5551 } 5552 else 5553 { 5554 // Window background 5555 if (!(flags & ImGuiWindowFlags_NoBackground)) 5556 { 5557 ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); 5558 bool override_alpha = false; 5559 float alpha = 1.0f; 5560 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) 5561 { 5562 alpha = g.NextWindowData.BgAlphaVal; 5563 override_alpha = true; 5564 } 5565 if (override_alpha) 5566 bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); 5567 window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); 5568 } 5569 5570 // Title bar 5571 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 5572 { 5573 ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); 5574 window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop); 5575 } 5576 5577 // Menu bar 5578 if (flags & ImGuiWindowFlags_MenuBar) 5579 { 5580 ImRect menu_bar_rect = window->MenuBarRect(); 5581 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. 5582 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, ImDrawFlags_RoundCornersTop); 5583 if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) 5584 window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); 5585 } 5586 5587 // Scrollbars 5588 if (window->ScrollbarX) 5589 Scrollbar(ImGuiAxis_X); 5590 if (window->ScrollbarY) 5591 Scrollbar(ImGuiAxis_Y); 5592 5593 // Render resize grips (after their input handling so we don't have a frame of latency) 5594 if (!(flags & ImGuiWindowFlags_NoResize)) 5595 { 5596 for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) 5597 { 5598 const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; 5599 const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); 5600 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))); 5601 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))); 5602 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); 5603 window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); 5604 } 5605 } 5606 5607 // Borders 5608 RenderWindowOuterBorders(window); 5609 } 5610 } 5611 5612 // Render title text, collapse button, close button 5613 void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) 5614 { 5615 ImGuiContext& g = *GImGui; 5616 ImGuiStyle& style = g.Style; 5617 ImGuiWindowFlags flags = window->Flags; 5618 5619 const bool has_close_button = (p_open != NULL); 5620 const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); 5621 5622 // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) 5623 const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; 5624 g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; 5625 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; 5626 5627 // Layout buttons 5628 // FIXME: Would be nice to generalize the subtleties expressed here into reusable code. 5629 float pad_l = style.FramePadding.x; 5630 float pad_r = style.FramePadding.x; 5631 float button_sz = g.FontSize; 5632 ImVec2 close_button_pos; 5633 ImVec2 collapse_button_pos; 5634 if (has_close_button) 5635 { 5636 pad_r += button_sz; 5637 close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); 5638 } 5639 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) 5640 { 5641 pad_r += button_sz; 5642 collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); 5643 } 5644 if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) 5645 { 5646 collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); 5647 pad_l += button_sz; 5648 } 5649 5650 // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) 5651 if (has_collapse_button) 5652 if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) 5653 window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function 5654 5655 // Close button 5656 if (has_close_button) 5657 if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) 5658 *p_open = false; 5659 5660 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 5661 g.CurrentItemFlags = item_flags_backup; 5662 5663 // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) 5664 // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. 5665 const char* UNSAVED_DOCUMENT_MARKER = "*"; 5666 const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; 5667 const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); 5668 5669 // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, 5670 // while uncentered title text will still reach edges correctly. 5671 if (pad_l > style.FramePadding.x) 5672 pad_l += g.Style.ItemInnerSpacing.x; 5673 if (pad_r > style.FramePadding.x) 5674 pad_r += g.Style.ItemInnerSpacing.x; 5675 if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f) 5676 { 5677 float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center 5678 float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x); 5679 pad_l = ImMax(pad_l, pad_extend * centerness); 5680 pad_r = ImMax(pad_r, pad_extend * centerness); 5681 } 5682 5683 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); 5684 ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); 5685 //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] 5686 //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] 5687 RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); 5688 if (flags & ImGuiWindowFlags_UnsavedDocument) 5689 { 5690 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); 5691 ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); 5692 RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); 5693 } 5694 } 5695 5696 void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) 5697 { 5698 window->ParentWindow = parent_window; 5699 window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; 5700 if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) 5701 window->RootWindow = parent_window->RootWindow; 5702 if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) 5703 window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; 5704 while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) 5705 { 5706 IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); 5707 window->RootWindowForNav = window->RootWindowForNav->ParentWindow; 5708 } 5709 } 5710 5711 // Push a new Dear ImGui window to add widgets to. 5712 // - 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. 5713 // - Begin/End can be called multiple times during the frame with the same window name to append content. 5714 // - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). 5715 // 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. 5716 // - 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. 5717 // - 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. 5718 bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) 5719 { 5720 ImGuiContext& g = *GImGui; 5721 const ImGuiStyle& style = g.Style; 5722 IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required 5723 IM_ASSERT(g.WithinFrameScope); // Forgot to call ImGui::NewFrame() 5724 IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet 5725 5726 // Find or create 5727 ImGuiWindow* window = FindWindowByName(name); 5728 const bool window_just_created = (window == NULL); 5729 if (window_just_created) 5730 window = CreateNewWindow(name, flags); 5731 5732 // Automatically disable manual moving/resizing when NoInputs is set 5733 if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) 5734 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 5735 5736 if (flags & ImGuiWindowFlags_NavFlattened) 5737 IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); 5738 5739 const int current_frame = g.FrameCount; 5740 const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); 5741 window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); 5742 5743 // Update the Appearing flag 5744 bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on 5745 if (flags & ImGuiWindowFlags_Popup) 5746 { 5747 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; 5748 window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed 5749 window_just_activated_by_user |= (window != popup_ref.Window); 5750 } 5751 window->Appearing = window_just_activated_by_user; 5752 if (window->Appearing) 5753 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); 5754 5755 // Update Flags, LastFrameActive, BeginOrderXXX fields 5756 if (first_begin_of_the_frame) 5757 { 5758 window->Flags = (ImGuiWindowFlags)flags; 5759 window->LastFrameActive = current_frame; 5760 window->LastTimeActive = (float)g.Time; 5761 window->BeginOrderWithinParent = 0; 5762 window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); 5763 } 5764 else 5765 { 5766 flags = window->Flags; 5767 } 5768 5769 // 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 5770 ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); 5771 ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; 5772 IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); 5773 5774 // We allow window memory to be compacted so recreate the base stack when needed. 5775 if (window->IDStack.Size == 0) 5776 window->IDStack.push_back(window->ID); 5777 5778 // Add to stack 5779 // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() 5780 g.CurrentWindowStack.push_back(window); 5781 g.CurrentWindow = window; 5782 window->DC.StackSizesOnBegin.SetToCurrentState(); 5783 g.CurrentWindow = NULL; 5784 5785 if (flags & ImGuiWindowFlags_Popup) 5786 { 5787 ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; 5788 popup_ref.Window = window; 5789 g.BeginPopupStack.push_back(popup_ref); 5790 window->PopupId = popup_ref.PopupId; 5791 } 5792 5793 // Update ->RootWindow and others pointers (before any possible call to FocusWindow) 5794 if (first_begin_of_the_frame) 5795 UpdateWindowParentAndRootLinks(window, flags, parent_window); 5796 5797 // Process SetNextWindow***() calls 5798 // (FIXME: Consider splitting the HasXXX flags into X/Y components 5799 bool window_pos_set_by_api = false; 5800 bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; 5801 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) 5802 { 5803 window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; 5804 if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) 5805 { 5806 // May be processed on the next frame if this is our first frame and we are measuring size 5807 // FIXME: Look into removing the branch so everything can go through this same code path for consistency. 5808 window->SetWindowPosVal = g.NextWindowData.PosVal; 5809 window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; 5810 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 5811 } 5812 else 5813 { 5814 SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); 5815 } 5816 } 5817 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) 5818 { 5819 window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); 5820 window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); 5821 SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); 5822 } 5823 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll) 5824 { 5825 if (g.NextWindowData.ScrollVal.x >= 0.0f) 5826 { 5827 window->ScrollTarget.x = g.NextWindowData.ScrollVal.x; 5828 window->ScrollTargetCenterRatio.x = 0.0f; 5829 } 5830 if (g.NextWindowData.ScrollVal.y >= 0.0f) 5831 { 5832 window->ScrollTarget.y = g.NextWindowData.ScrollVal.y; 5833 window->ScrollTargetCenterRatio.y = 0.0f; 5834 } 5835 } 5836 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize) 5837 window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; 5838 else if (first_begin_of_the_frame) 5839 window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); 5840 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) 5841 SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); 5842 if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) 5843 FocusWindow(window); 5844 if (window->Appearing) 5845 SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); 5846 5847 // When reusing window again multiple times a frame, just append content (don't need to setup again) 5848 if (first_begin_of_the_frame) 5849 { 5850 // Initialize 5851 const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) 5852 window->Active = true; 5853 window->HasCloseButton = (p_open != NULL); 5854 window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); 5855 window->IDStack.resize(1); 5856 window->DrawList->_ResetForNewFrame(); 5857 window->DC.CurrentTableIdx = -1; 5858 5859 // Restore buffer capacity when woken from a compacted state, to avoid 5860 if (window->MemoryCompacted) 5861 GcAwakeTransientWindowBuffers(window); 5862 5863 // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). 5864 // 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. 5865 bool window_title_visible_elsewhere = false; 5866 if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB 5867 window_title_visible_elsewhere = true; 5868 if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) 5869 { 5870 size_t buf_len = (size_t)window->NameBufLen; 5871 window->Name = ImStrdupcpy(window->Name, &buf_len, name); 5872 window->NameBufLen = (int)buf_len; 5873 } 5874 5875 // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS 5876 5877 // Update contents size from last frame for auto-fitting (or use explicit size) 5878 const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); 5879 CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); 5880 if (window->HiddenFramesCanSkipItems > 0) 5881 window->HiddenFramesCanSkipItems--; 5882 if (window->HiddenFramesCannotSkipItems > 0) 5883 window->HiddenFramesCannotSkipItems--; 5884 if (window->HiddenFramesForRenderOnly > 0) 5885 window->HiddenFramesForRenderOnly--; 5886 5887 // Hide new windows for one frame until they calculate their size 5888 if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) 5889 window->HiddenFramesCannotSkipItems = 1; 5890 5891 // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) 5892 // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. 5893 if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) 5894 { 5895 window->HiddenFramesCannotSkipItems = 1; 5896 if (flags & ImGuiWindowFlags_AlwaysAutoResize) 5897 { 5898 if (!window_size_x_set_by_api) 5899 window->Size.x = window->SizeFull.x = 0.f; 5900 if (!window_size_y_set_by_api) 5901 window->Size.y = window->SizeFull.y = 0.f; 5902 window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f); 5903 } 5904 } 5905 5906 // SELECT VIEWPORT 5907 // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) 5908 SetCurrentWindow(window); 5909 5910 // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) 5911 5912 if (flags & ImGuiWindowFlags_ChildWindow) 5913 window->WindowBorderSize = style.ChildBorderSize; 5914 else 5915 window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; 5916 window->WindowPadding = style.WindowPadding; 5917 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) 5918 window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); 5919 5920 // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. 5921 window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); 5922 window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; 5923 5924 // Collapse window by double-clicking on title bar 5925 // 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 5926 if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) 5927 { 5928 // 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. 5929 ImRect title_bar_rect = window->TitleBarRect(); 5930 if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) 5931 window->WantCollapseToggle = true; 5932 if (window->WantCollapseToggle) 5933 { 5934 window->Collapsed = !window->Collapsed; 5935 MarkIniSettingsDirty(window); 5936 } 5937 } 5938 else 5939 { 5940 window->Collapsed = false; 5941 } 5942 window->WantCollapseToggle = false; 5943 5944 // SIZE 5945 5946 // Calculate auto-fit size, handle automatic resize 5947 const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal); 5948 bool use_current_size_for_scrollbar_x = window_just_created; 5949 bool use_current_size_for_scrollbar_y = window_just_created; 5950 if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) 5951 { 5952 // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. 5953 if (!window_size_x_set_by_api) 5954 { 5955 window->SizeFull.x = size_auto_fit.x; 5956 use_current_size_for_scrollbar_x = true; 5957 } 5958 if (!window_size_y_set_by_api) 5959 { 5960 window->SizeFull.y = size_auto_fit.y; 5961 use_current_size_for_scrollbar_y = true; 5962 } 5963 } 5964 else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) 5965 { 5966 // Auto-fit may only grow window during the first few frames 5967 // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. 5968 if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) 5969 { 5970 window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; 5971 use_current_size_for_scrollbar_x = true; 5972 } 5973 if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) 5974 { 5975 window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; 5976 use_current_size_for_scrollbar_y = true; 5977 } 5978 if (!window->Collapsed) 5979 MarkIniSettingsDirty(window); 5980 } 5981 5982 // Apply minimum/maximum window size constraints and final size 5983 window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); 5984 window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; 5985 5986 // Decoration size 5987 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); 5988 5989 // POSITION 5990 5991 // Popup latch its initial position, will position itself when it appears next frame 5992 if (window_just_activated_by_user) 5993 { 5994 window->AutoPosLastDirection = ImGuiDir_None; 5995 if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos() 5996 window->Pos = g.BeginPopupStack.back().OpenPopupPos; 5997 } 5998 5999 // Position child window 6000 if (flags & ImGuiWindowFlags_ChildWindow) 6001 { 6002 IM_ASSERT(parent_window && parent_window->Active); 6003 window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; 6004 parent_window->DC.ChildWindows.push_back(window); 6005 if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) 6006 window->Pos = parent_window->DC.CursorPos; 6007 } 6008 6009 const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); 6010 if (window_pos_with_pivot) 6011 SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) 6012 else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) 6013 window->Pos = FindBestWindowPosForPopup(window); 6014 else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) 6015 window->Pos = FindBestWindowPosForPopup(window); 6016 else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) 6017 window->Pos = FindBestWindowPosForPopup(window); 6018 6019 // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) 6020 // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. 6021 ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); 6022 ImRect viewport_rect(viewport->GetMainRect()); 6023 ImRect viewport_work_rect(viewport->GetWorkRect()); 6024 ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); 6025 ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); 6026 6027 // Clamp position/size so window stays visible within its viewport or monitor 6028 // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. 6029 if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) 6030 if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) 6031 ClampWindowRect(window, visibility_rect); 6032 window->Pos = ImFloor(window->Pos); 6033 6034 // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) 6035 // Large values tend to lead to variety of artifacts and are not recommended. 6036 window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; 6037 6038 // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts. 6039 //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar)) 6040 // window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f); 6041 6042 // Apply window focus (new and reactivated windows are moved to front) 6043 bool want_focus = false; 6044 if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) 6045 { 6046 if (flags & ImGuiWindowFlags_Popup) 6047 want_focus = true; 6048 else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) 6049 want_focus = true; 6050 } 6051 6052 // Handle manual resize: Resize Grips, Borders, Gamepad 6053 int border_held = -1; 6054 ImU32 resize_grip_col[4] = {}; 6055 const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. 6056 const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); 6057 if (!window->Collapsed) 6058 if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) 6059 use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; 6060 window->ResizeBorderHeld = (signed char)border_held; 6061 6062 // SCROLLBAR VISIBILITY 6063 6064 // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). 6065 if (!window->Collapsed) 6066 { 6067 // When reading the current size we need to read it after size constraints have been applied. 6068 // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. 6069 ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); 6070 ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; 6071 ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; 6072 float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; 6073 float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; 6074 //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? 6075 window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); 6076 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)); 6077 if (window->ScrollbarX && !window->ScrollbarY) 6078 window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); 6079 window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); 6080 } 6081 6082 // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) 6083 // Update various regions. Variables they depends on should be set above in this function. 6084 // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. 6085 6086 // Outer rectangle 6087 // Not affected by window border size. Used by: 6088 // - FindHoveredWindow() (w/ extra padding when border resize is enabled) 6089 // - Begin() initial clipping rect for drawing window background and borders. 6090 // - Begin() clipping whole child 6091 const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect; 6092 const ImRect outer_rect = window->Rect(); 6093 const ImRect title_bar_rect = window->TitleBarRect(); 6094 window->OuterRectClipped = outer_rect; 6095 window->OuterRectClipped.ClipWith(host_rect); 6096 6097 // Inner rectangle 6098 // Not affected by window border size. Used by: 6099 // - InnerClipRect 6100 // - ScrollToBringRectIntoView() 6101 // - NavUpdatePageUpPageDown() 6102 // - Scrollbar() 6103 window->InnerRect.Min.x = window->Pos.x; 6104 window->InnerRect.Min.y = window->Pos.y + decoration_up_height; 6105 window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; 6106 window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; 6107 6108 // Inner clipping rectangle. 6109 // Will extend a little bit outside the normal work region. 6110 // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space. 6111 // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. 6112 // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. 6113 // Affected by window/frame border size. Used by: 6114 // - Begin() initial clip rect 6115 float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); 6116 window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); 6117 window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); 6118 window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); 6119 window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); 6120 window->InnerClipRect.ClipWithFull(host_rect); 6121 6122 // Default item width. Make it proportional to window size if window manually resizes 6123 if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) 6124 window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f); 6125 else 6126 window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f); 6127 6128 // SCROLLING 6129 6130 // Lock down maximum scrolling 6131 // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate 6132 // for right/bottom aligned items without creating a scrollbar. 6133 window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth()); 6134 window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight()); 6135 6136 // Apply scrolling 6137 window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); 6138 window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); 6139 6140 // DRAWING 6141 6142 // Setup draw list and outer clipping rectangle 6143 IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); 6144 window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); 6145 PushClipRect(host_rect.Min, host_rect.Max, false); 6146 6147 // Draw modal window background (darkens what is behind them, all viewports) 6148 const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; 6149 const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); 6150 if (dim_bg_for_modal || dim_bg_for_window_list) 6151 { 6152 const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); 6153 window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); 6154 } 6155 6156 // Draw navigation selection/windowing rectangle background 6157 if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) 6158 { 6159 ImRect bb = window->Rect(); 6160 bb.Expand(g.FontSize); 6161 if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway 6162 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); 6163 } 6164 6165 // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. 6166 // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. 6167 // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. 6168 // We also disabled this when we have dimming overlay behind this specific one child. 6169 // 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. 6170 { 6171 bool render_decorations_in_parent = false; 6172 if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) 6173 if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) 6174 render_decorations_in_parent = true; 6175 if (render_decorations_in_parent) 6176 window->DrawList = parent_window->DrawList; 6177 6178 // Handle title bar, scrollbar, resize grips and resize borders 6179 const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; 6180 const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); 6181 RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); 6182 6183 if (render_decorations_in_parent) 6184 window->DrawList = &window->DrawListInst; 6185 } 6186 6187 // Draw navigation selection/windowing rectangle border 6188 if (g.NavWindowingTargetAnim == window) 6189 { 6190 float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); 6191 ImRect bb = window->Rect(); 6192 bb.Expand(g.FontSize); 6193 if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward 6194 { 6195 bb.Expand(-g.FontSize - 1.0f); 6196 rounding = window->WindowRounding; 6197 } 6198 window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); 6199 } 6200 6201 // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) 6202 6203 // Work rectangle. 6204 // Affected by window padding and border size. Used by: 6205 // - Columns() for right-most edge 6206 // - TreeNode(), CollapsingHeader() for right-most edge 6207 // - BeginTabBar() for right-most edge 6208 const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); 6209 const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); 6210 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)); 6211 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)); 6212 window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); 6213 window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); 6214 window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; 6215 window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; 6216 window->ParentWorkRect = window->WorkRect; 6217 6218 // [LEGACY] Content Region 6219 // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. 6220 // Used by: 6221 // - Mouse wheel scrolling + many other things 6222 window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; 6223 window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; 6224 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)); 6225 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)); 6226 6227 // Setup drawing context 6228 // (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.) 6229 window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; 6230 window->DC.GroupOffset.x = 0.0f; 6231 window->DC.ColumnsOffset.x = 0.0f; 6232 window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); 6233 window->DC.CursorPos = window->DC.CursorStartPos; 6234 window->DC.CursorPosPrevLine = window->DC.CursorPos; 6235 window->DC.CursorMaxPos = window->DC.CursorStartPos; 6236 window->DC.IdealMaxPos = window->DC.CursorStartPos; 6237 window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); 6238 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; 6239 6240 window->DC.NavLayerCurrent = ImGuiNavLayer_Main; 6241 window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; 6242 window->DC.NavLayersActiveMaskNext = 0x00; 6243 window->DC.NavHideHighlightOneFrame = false; 6244 window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); 6245 6246 window->DC.MenuBarAppending = false; 6247 window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); 6248 window->DC.TreeDepth = 0; 6249 window->DC.TreeJumpToParentOnPopMask = 0x00; 6250 window->DC.ChildWindows.resize(0); 6251 window->DC.StateStorage = &window->StateStorage; 6252 window->DC.CurrentColumns = NULL; 6253 window->DC.LayoutType = ImGuiLayoutType_Vertical; 6254 window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; 6255 window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; 6256 6257 window->DC.ItemWidth = window->ItemWidthDefault; 6258 window->DC.TextWrapPos = -1.0f; // disabled 6259 window->DC.ItemWidthStack.resize(0); 6260 window->DC.TextWrapPosStack.resize(0); 6261 6262 if (window->AutoFitFramesX > 0) 6263 window->AutoFitFramesX--; 6264 if (window->AutoFitFramesY > 0) 6265 window->AutoFitFramesY--; 6266 6267 // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) 6268 if (want_focus) 6269 { 6270 FocusWindow(window); 6271 NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls 6272 } 6273 6274 // Title bar 6275 if (!(flags & ImGuiWindowFlags_NoTitleBar)) 6276 RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); 6277 6278 // Clear hit test shape every frame 6279 window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; 6280 6281 // Pressing CTRL+C while holding on a window copy its content to the clipboard 6282 // 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. 6283 // Maybe we can support CTRL+C on every element? 6284 /* 6285 //if (g.NavWindow == window && g.ActiveId == 0) 6286 if (g.ActiveId == window->MoveId) 6287 if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) 6288 LogToClipboard(); 6289 */ 6290 6291 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). 6292 // This is useful to allow creating context menus on title bar only, etc. 6293 SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); 6294 6295 #ifdef IMGUI_ENABLE_TEST_ENGINE 6296 if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) 6297 IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); 6298 #endif 6299 } 6300 else 6301 { 6302 // Append 6303 SetCurrentWindow(window); 6304 } 6305 6306 // Pull/inherit current state 6307 g.CurrentItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack 6308 window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 6309 6310 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); 6311 6312 // 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) 6313 window->WriteAccessed = false; 6314 window->BeginCount++; 6315 g.NextWindowData.ClearFlags(); 6316 6317 // Update visibility 6318 if (first_begin_of_the_frame) 6319 { 6320 if (flags & ImGuiWindowFlags_ChildWindow) 6321 { 6322 // Child window can be out of sight and have "negative" clip windows. 6323 // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). 6324 IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); 6325 if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow?? 6326 if (!g.LogEnabled) 6327 if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) 6328 window->HiddenFramesCanSkipItems = 1; 6329 6330 // Hide along with parent or if parent is collapsed 6331 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) 6332 window->HiddenFramesCanSkipItems = 1; 6333 if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) 6334 window->HiddenFramesCannotSkipItems = 1; 6335 } 6336 6337 // 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) 6338 if (style.Alpha <= 0.0f) 6339 window->HiddenFramesCanSkipItems = 1; 6340 6341 // Update the Hidden flag 6342 window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); 6343 6344 // Disable inputs for requested number of frames 6345 if (window->DisableInputsFrames > 0) 6346 { 6347 window->DisableInputsFrames--; 6348 window->Flags |= ImGuiWindowFlags_NoInputs; 6349 } 6350 6351 // Update the SkipItems flag, used to early out of all items functions (no layout required) 6352 bool skip_items = false; 6353 if (window->Collapsed || !window->Active || window->Hidden) 6354 if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) 6355 skip_items = true; 6356 window->SkipItems = skip_items; 6357 } 6358 6359 return !window->SkipItems; 6360 } 6361 6362 void ImGui::End() 6363 { 6364 ImGuiContext& g = *GImGui; 6365 ImGuiWindow* window = g.CurrentWindow; 6366 6367 // Error checking: verify that user hasn't called End() too many times! 6368 if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow) 6369 { 6370 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!"); 6371 return; 6372 } 6373 IM_ASSERT(g.CurrentWindowStack.Size > 0); 6374 6375 // Error checking: verify that user doesn't directly call End() on a child window. 6376 if (window->Flags & ImGuiWindowFlags_ChildWindow) 6377 IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); 6378 6379 // Close anything that is open 6380 if (window->DC.CurrentColumns) 6381 EndColumns(); 6382 PopClipRect(); // Inner window clip rectangle 6383 6384 // Stop logging 6385 if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging 6386 LogFinish(); 6387 6388 // Pop from window stack 6389 g.CurrentWindowStack.pop_back(); 6390 if (window->Flags & ImGuiWindowFlags_Popup) 6391 g.BeginPopupStack.pop_back(); 6392 window->DC.StackSizesOnBegin.CompareWithCurrentState(); 6393 SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); 6394 } 6395 6396 void ImGui::BringWindowToFocusFront(ImGuiWindow* window) 6397 { 6398 ImGuiContext& g = *GImGui; 6399 IM_ASSERT(window == window->RootWindow); 6400 6401 const int cur_order = window->FocusOrder; 6402 IM_ASSERT(g.WindowsFocusOrder[cur_order] == window); 6403 if (g.WindowsFocusOrder.back() == window) 6404 return; 6405 6406 const int new_order = g.WindowsFocusOrder.Size - 1; 6407 for (int n = cur_order; n < new_order; n++) 6408 { 6409 g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1]; 6410 g.WindowsFocusOrder[n]->FocusOrder--; 6411 IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n); 6412 } 6413 g.WindowsFocusOrder[new_order] = window; 6414 window->FocusOrder = (short)new_order; 6415 } 6416 6417 void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) 6418 { 6419 ImGuiContext& g = *GImGui; 6420 ImGuiWindow* current_front_window = g.Windows.back(); 6421 if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better) 6422 return; 6423 for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window 6424 if (g.Windows[i] == window) 6425 { 6426 memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); 6427 g.Windows[g.Windows.Size - 1] = window; 6428 break; 6429 } 6430 } 6431 6432 void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) 6433 { 6434 ImGuiContext& g = *GImGui; 6435 if (g.Windows[0] == window) 6436 return; 6437 for (int i = 0; i < g.Windows.Size; i++) 6438 if (g.Windows[i] == window) 6439 { 6440 memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); 6441 g.Windows[0] = window; 6442 break; 6443 } 6444 } 6445 6446 // Moving window to front of display and set focus (which happens to be back of our sorted list) 6447 void ImGui::FocusWindow(ImGuiWindow* window) 6448 { 6449 ImGuiContext& g = *GImGui; 6450 6451 if (g.NavWindow != window) 6452 { 6453 g.NavWindow = window; 6454 if (window && g.NavDisableMouseHover) 6455 g.NavMousePosDirty = true; 6456 g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId 6457 g.NavFocusScopeId = 0; 6458 g.NavIdIsAlive = false; 6459 g.NavLayer = ImGuiNavLayer_Main; 6460 g.NavInitRequest = g.NavMoveRequest = false; 6461 NavUpdateAnyRequestFlag(); 6462 //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); 6463 } 6464 6465 // Close popups if any 6466 ClosePopupsOverWindow(window, false); 6467 6468 // Move the root window to the top of the pile 6469 IM_ASSERT(window == NULL || window->RootWindow != NULL); 6470 ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop 6471 ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; 6472 6473 // Steal active widgets. Some of the cases it triggers includes: 6474 // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. 6475 // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) 6476 if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) 6477 if (!g.ActiveIdNoClearOnFocusLoss) 6478 ClearActiveID(); 6479 6480 // Passing NULL allow to disable keyboard focus 6481 if (!window) 6482 return; 6483 6484 // Bring to front 6485 BringWindowToFocusFront(focus_front_window); 6486 if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) 6487 BringWindowToDisplayFront(display_front_window); 6488 } 6489 6490 void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) 6491 { 6492 ImGuiContext& g = *GImGui; 6493 6494 const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; 6495 for (int i = start_idx; i >= 0; i--) 6496 { 6497 // 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. 6498 ImGuiWindow* window = g.WindowsFocusOrder[i]; 6499 IM_ASSERT(window == window->RootWindow); 6500 if (window != ignore_window && window->WasActive) 6501 if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) 6502 { 6503 ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); 6504 FocusWindow(focus_window); 6505 return; 6506 } 6507 } 6508 FocusWindow(NULL); 6509 } 6510 6511 // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. 6512 void ImGui::SetCurrentFont(ImFont* font) 6513 { 6514 ImGuiContext& g = *GImGui; 6515 IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? 6516 IM_ASSERT(font->Scale > 0.0f); 6517 g.Font = font; 6518 g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); 6519 g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; 6520 6521 ImFontAtlas* atlas = g.Font->ContainerAtlas; 6522 g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; 6523 g.DrawListSharedData.TexUvLines = atlas->TexUvLines; 6524 g.DrawListSharedData.Font = g.Font; 6525 g.DrawListSharedData.FontSize = g.FontSize; 6526 } 6527 6528 void ImGui::PushFont(ImFont* font) 6529 { 6530 ImGuiContext& g = *GImGui; 6531 if (!font) 6532 font = GetDefaultFont(); 6533 SetCurrentFont(font); 6534 g.FontStack.push_back(font); 6535 g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); 6536 } 6537 6538 void ImGui::PopFont() 6539 { 6540 ImGuiContext& g = *GImGui; 6541 g.CurrentWindow->DrawList->PopTextureID(); 6542 g.FontStack.pop_back(); 6543 SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); 6544 } 6545 6546 void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) 6547 { 6548 ImGuiContext& g = *GImGui; 6549 ImGuiItemFlags item_flags = g.CurrentItemFlags; 6550 IM_ASSERT(item_flags == g.ItemFlagsStack.back()); 6551 if (enabled) 6552 item_flags |= option; 6553 else 6554 item_flags &= ~option; 6555 g.CurrentItemFlags = item_flags; 6556 g.ItemFlagsStack.push_back(item_flags); 6557 } 6558 6559 void ImGui::PopItemFlag() 6560 { 6561 ImGuiContext& g = *GImGui; 6562 IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. 6563 g.ItemFlagsStack.pop_back(); 6564 g.CurrentItemFlags = g.ItemFlagsStack.back(); 6565 } 6566 6567 // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. 6568 void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) 6569 { 6570 PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus); 6571 } 6572 6573 void ImGui::PopAllowKeyboardFocus() 6574 { 6575 PopItemFlag(); 6576 } 6577 6578 void ImGui::PushButtonRepeat(bool repeat) 6579 { 6580 PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); 6581 } 6582 6583 void ImGui::PopButtonRepeat() 6584 { 6585 PopItemFlag(); 6586 } 6587 6588 void ImGui::PushTextWrapPos(float wrap_pos_x) 6589 { 6590 ImGuiWindow* window = GetCurrentWindow(); 6591 window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); 6592 window->DC.TextWrapPos = wrap_pos_x; 6593 } 6594 6595 void ImGui::PopTextWrapPos() 6596 { 6597 ImGuiWindow* window = GetCurrentWindow(); 6598 window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); 6599 window->DC.TextWrapPosStack.pop_back(); 6600 } 6601 6602 bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) 6603 { 6604 if (window->RootWindow == potential_parent) 6605 return true; 6606 while (window != NULL) 6607 { 6608 if (window == potential_parent) 6609 return true; 6610 window = window->ParentWindow; 6611 } 6612 return false; 6613 } 6614 6615 bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) 6616 { 6617 ImGuiContext& g = *GImGui; 6618 for (int i = g.Windows.Size - 1; i >= 0; i--) 6619 { 6620 ImGuiWindow* candidate_window = g.Windows[i]; 6621 if (candidate_window == potential_above) 6622 return true; 6623 if (candidate_window == potential_below) 6624 return false; 6625 } 6626 return false; 6627 } 6628 6629 bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) 6630 { 6631 IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function 6632 ImGuiContext& g = *GImGui; 6633 if (g.HoveredWindow == NULL) 6634 return false; 6635 6636 if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) 6637 { 6638 ImGuiWindow* window = g.CurrentWindow; 6639 switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) 6640 { 6641 case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: 6642 if (g.HoveredWindow->RootWindow != window->RootWindow) 6643 return false; 6644 break; 6645 case ImGuiHoveredFlags_RootWindow: 6646 if (g.HoveredWindow != window->RootWindow) 6647 return false; 6648 break; 6649 case ImGuiHoveredFlags_ChildWindows: 6650 if (!IsWindowChildOf(g.HoveredWindow, window)) 6651 return false; 6652 break; 6653 default: 6654 if (g.HoveredWindow != window) 6655 return false; 6656 break; 6657 } 6658 } 6659 6660 if (!IsWindowContentHoverable(g.HoveredWindow, flags)) 6661 return false; 6662 if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) 6663 if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) 6664 return false; 6665 return true; 6666 } 6667 6668 bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) 6669 { 6670 ImGuiContext& g = *GImGui; 6671 6672 if (flags & ImGuiFocusedFlags_AnyWindow) 6673 return g.NavWindow != NULL; 6674 6675 IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() 6676 switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) 6677 { 6678 case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: 6679 return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; 6680 case ImGuiFocusedFlags_RootWindow: 6681 return g.NavWindow == g.CurrentWindow->RootWindow; 6682 case ImGuiFocusedFlags_ChildWindows: 6683 return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); 6684 default: 6685 return g.NavWindow == g.CurrentWindow; 6686 } 6687 } 6688 6689 // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) 6690 // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically. 6691 // If you want a window to never be focused, you may use the e.g. NoInputs flag. 6692 bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) 6693 { 6694 return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); 6695 } 6696 6697 float ImGui::GetWindowWidth() 6698 { 6699 ImGuiWindow* window = GImGui->CurrentWindow; 6700 return window->Size.x; 6701 } 6702 6703 float ImGui::GetWindowHeight() 6704 { 6705 ImGuiWindow* window = GImGui->CurrentWindow; 6706 return window->Size.y; 6707 } 6708 6709 ImVec2 ImGui::GetWindowPos() 6710 { 6711 ImGuiContext& g = *GImGui; 6712 ImGuiWindow* window = g.CurrentWindow; 6713 return window->Pos; 6714 } 6715 6716 void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) 6717 { 6718 // Test condition (NB: bit 0 is always true) and clear flags for next time 6719 if (cond && (window->SetWindowPosAllowFlags & cond) == 0) 6720 return; 6721 6722 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6723 window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6724 window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); 6725 6726 // Set 6727 const ImVec2 old_pos = window->Pos; 6728 window->Pos = ImFloor(pos); 6729 ImVec2 offset = window->Pos - old_pos; 6730 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 6731 window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. 6732 window->DC.IdealMaxPos += offset; 6733 window->DC.CursorStartPos += offset; 6734 } 6735 6736 void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) 6737 { 6738 ImGuiWindow* window = GetCurrentWindowRead(); 6739 SetWindowPos(window, pos, cond); 6740 } 6741 6742 void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond) 6743 { 6744 if (ImGuiWindow* window = FindWindowByName(name)) 6745 SetWindowPos(window, pos, cond); 6746 } 6747 6748 ImVec2 ImGui::GetWindowSize() 6749 { 6750 ImGuiWindow* window = GetCurrentWindowRead(); 6751 return window->Size; 6752 } 6753 6754 void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) 6755 { 6756 // Test condition (NB: bit 0 is always true) and clear flags for next time 6757 if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) 6758 return; 6759 6760 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6761 window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6762 6763 // Set 6764 if (size.x > 0.0f) 6765 { 6766 window->AutoFitFramesX = 0; 6767 window->SizeFull.x = IM_FLOOR(size.x); 6768 } 6769 else 6770 { 6771 window->AutoFitFramesX = 2; 6772 window->AutoFitOnlyGrows = false; 6773 } 6774 if (size.y > 0.0f) 6775 { 6776 window->AutoFitFramesY = 0; 6777 window->SizeFull.y = IM_FLOOR(size.y); 6778 } 6779 else 6780 { 6781 window->AutoFitFramesY = 2; 6782 window->AutoFitOnlyGrows = false; 6783 } 6784 } 6785 6786 void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) 6787 { 6788 SetWindowSize(GImGui->CurrentWindow, size, cond); 6789 } 6790 6791 void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) 6792 { 6793 if (ImGuiWindow* window = FindWindowByName(name)) 6794 SetWindowSize(window, size, cond); 6795 } 6796 6797 void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) 6798 { 6799 // Test condition (NB: bit 0 is always true) and clear flags for next time 6800 if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) 6801 return; 6802 window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); 6803 6804 // Set 6805 window->Collapsed = collapsed; 6806 } 6807 6808 void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) 6809 { 6810 IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters 6811 window->HitTestHoleSize = ImVec2ih(size); 6812 window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); 6813 } 6814 6815 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) 6816 { 6817 SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); 6818 } 6819 6820 bool ImGui::IsWindowCollapsed() 6821 { 6822 ImGuiWindow* window = GetCurrentWindowRead(); 6823 return window->Collapsed; 6824 } 6825 6826 bool ImGui::IsWindowAppearing() 6827 { 6828 ImGuiWindow* window = GetCurrentWindowRead(); 6829 return window->Appearing; 6830 } 6831 6832 void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) 6833 { 6834 if (ImGuiWindow* window = FindWindowByName(name)) 6835 SetWindowCollapsed(window, collapsed, cond); 6836 } 6837 6838 void ImGui::SetWindowFocus() 6839 { 6840 FocusWindow(GImGui->CurrentWindow); 6841 } 6842 6843 void ImGui::SetWindowFocus(const char* name) 6844 { 6845 if (name) 6846 { 6847 if (ImGuiWindow* window = FindWindowByName(name)) 6848 FocusWindow(window); 6849 } 6850 else 6851 { 6852 FocusWindow(NULL); 6853 } 6854 } 6855 6856 void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) 6857 { 6858 ImGuiContext& g = *GImGui; 6859 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6860 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos; 6861 g.NextWindowData.PosVal = pos; 6862 g.NextWindowData.PosPivotVal = pivot; 6863 g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; 6864 } 6865 6866 void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) 6867 { 6868 ImGuiContext& g = *GImGui; 6869 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6870 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize; 6871 g.NextWindowData.SizeVal = size; 6872 g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; 6873 } 6874 6875 void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) 6876 { 6877 ImGuiContext& g = *GImGui; 6878 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; 6879 g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); 6880 g.NextWindowData.SizeCallback = custom_callback; 6881 g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; 6882 } 6883 6884 // Content size = inner scrollable rectangle, padded with WindowPadding. 6885 // SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item. 6886 void ImGui::SetNextWindowContentSize(const ImVec2& size) 6887 { 6888 ImGuiContext& g = *GImGui; 6889 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize; 6890 g.NextWindowData.ContentSizeVal = ImFloor(size); 6891 } 6892 6893 void ImGui::SetNextWindowScroll(const ImVec2& scroll) 6894 { 6895 ImGuiContext& g = *GImGui; 6896 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll; 6897 g.NextWindowData.ScrollVal = scroll; 6898 } 6899 6900 void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) 6901 { 6902 ImGuiContext& g = *GImGui; 6903 IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. 6904 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed; 6905 g.NextWindowData.CollapsedVal = collapsed; 6906 g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; 6907 } 6908 6909 void ImGui::SetNextWindowFocus() 6910 { 6911 ImGuiContext& g = *GImGui; 6912 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus; 6913 } 6914 6915 void ImGui::SetNextWindowBgAlpha(float alpha) 6916 { 6917 ImGuiContext& g = *GImGui; 6918 g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha; 6919 g.NextWindowData.BgAlphaVal = alpha; 6920 } 6921 6922 ImDrawList* ImGui::GetWindowDrawList() 6923 { 6924 ImGuiWindow* window = GetCurrentWindow(); 6925 return window->DrawList; 6926 } 6927 6928 ImFont* ImGui::GetFont() 6929 { 6930 return GImGui->Font; 6931 } 6932 6933 float ImGui::GetFontSize() 6934 { 6935 return GImGui->FontSize; 6936 } 6937 6938 ImVec2 ImGui::GetFontTexUvWhitePixel() 6939 { 6940 return GImGui->DrawListSharedData.TexUvWhitePixel; 6941 } 6942 6943 void ImGui::SetWindowFontScale(float scale) 6944 { 6945 IM_ASSERT(scale > 0.0f); 6946 ImGuiContext& g = *GImGui; 6947 ImGuiWindow* window = GetCurrentWindow(); 6948 window->FontWindowScale = scale; 6949 g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); 6950 } 6951 6952 void ImGui::ActivateItem(ImGuiID id) 6953 { 6954 ImGuiContext& g = *GImGui; 6955 g.NavNextActivateId = id; 6956 } 6957 6958 void ImGui::PushFocusScope(ImGuiID id) 6959 { 6960 ImGuiContext& g = *GImGui; 6961 ImGuiWindow* window = g.CurrentWindow; 6962 g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent); 6963 window->DC.NavFocusScopeIdCurrent = id; 6964 } 6965 6966 void ImGui::PopFocusScope() 6967 { 6968 ImGuiContext& g = *GImGui; 6969 ImGuiWindow* window = g.CurrentWindow; 6970 IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ? 6971 window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back(); 6972 g.FocusScopeStack.pop_back(); 6973 } 6974 6975 void ImGui::SetKeyboardFocusHere(int offset) 6976 { 6977 IM_ASSERT(offset >= -1); // -1 is allowed but not below 6978 ImGuiContext& g = *GImGui; 6979 ImGuiWindow* window = g.CurrentWindow; 6980 g.TabFocusRequestNextWindow = window; 6981 g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; 6982 g.TabFocusRequestNextCounterTabStop = INT_MAX; 6983 } 6984 6985 void ImGui::SetItemDefaultFocus() 6986 { 6987 ImGuiContext& g = *GImGui; 6988 ImGuiWindow* window = g.CurrentWindow; 6989 if (!window->Appearing) 6990 return; 6991 if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == window->DC.NavLayerCurrent) 6992 { 6993 g.NavInitRequest = false; 6994 g.NavInitResultId = window->DC.LastItemId; 6995 g.NavInitResultRectRel = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); 6996 NavUpdateAnyRequestFlag(); 6997 if (!IsItemVisible()) 6998 SetScrollHereY(); 6999 } 7000 } 7001 7002 void ImGui::SetStateStorage(ImGuiStorage* tree) 7003 { 7004 ImGuiWindow* window = GImGui->CurrentWindow; 7005 window->DC.StateStorage = tree ? tree : &window->StateStorage; 7006 } 7007 7008 ImGuiStorage* ImGui::GetStateStorage() 7009 { 7010 ImGuiWindow* window = GImGui->CurrentWindow; 7011 return window->DC.StateStorage; 7012 } 7013 7014 void ImGui::PushID(const char* str_id) 7015 { 7016 ImGuiContext& g = *GImGui; 7017 ImGuiWindow* window = g.CurrentWindow; 7018 ImGuiID id = window->GetIDNoKeepAlive(str_id); 7019 window->IDStack.push_back(id); 7020 } 7021 7022 void ImGui::PushID(const char* str_id_begin, const char* str_id_end) 7023 { 7024 ImGuiContext& g = *GImGui; 7025 ImGuiWindow* window = g.CurrentWindow; 7026 ImGuiID id = window->GetIDNoKeepAlive(str_id_begin, str_id_end); 7027 window->IDStack.push_back(id); 7028 } 7029 7030 void ImGui::PushID(const void* ptr_id) 7031 { 7032 ImGuiContext& g = *GImGui; 7033 ImGuiWindow* window = g.CurrentWindow; 7034 ImGuiID id = window->GetIDNoKeepAlive(ptr_id); 7035 window->IDStack.push_back(id); 7036 } 7037 7038 void ImGui::PushID(int int_id) 7039 { 7040 ImGuiContext& g = *GImGui; 7041 ImGuiWindow* window = g.CurrentWindow; 7042 ImGuiID id = window->GetIDNoKeepAlive(int_id); 7043 window->IDStack.push_back(id); 7044 } 7045 7046 // Push a given id value ignoring the ID stack as a seed. 7047 void ImGui::PushOverrideID(ImGuiID id) 7048 { 7049 ImGuiContext& g = *GImGui; 7050 ImGuiWindow* window = g.CurrentWindow; 7051 window->IDStack.push_back(id); 7052 } 7053 7054 // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call 7055 // (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level. 7056 // for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more) 7057 ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) 7058 { 7059 ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); 7060 ImGui::KeepAliveID(id); 7061 #ifdef IMGUI_ENABLE_TEST_ENGINE 7062 ImGuiContext& g = *GImGui; 7063 IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); 7064 #endif 7065 return id; 7066 } 7067 7068 void ImGui::PopID() 7069 { 7070 ImGuiWindow* window = GImGui->CurrentWindow; 7071 IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window? 7072 window->IDStack.pop_back(); 7073 } 7074 7075 ImGuiID ImGui::GetID(const char* str_id) 7076 { 7077 ImGuiWindow* window = GImGui->CurrentWindow; 7078 return window->GetID(str_id); 7079 } 7080 7081 ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) 7082 { 7083 ImGuiWindow* window = GImGui->CurrentWindow; 7084 return window->GetID(str_id_begin, str_id_end); 7085 } 7086 7087 ImGuiID ImGui::GetID(const void* ptr_id) 7088 { 7089 ImGuiWindow* window = GImGui->CurrentWindow; 7090 return window->GetID(ptr_id); 7091 } 7092 7093 bool ImGui::IsRectVisible(const ImVec2& size) 7094 { 7095 ImGuiWindow* window = GImGui->CurrentWindow; 7096 return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); 7097 } 7098 7099 bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) 7100 { 7101 ImGuiWindow* window = GImGui->CurrentWindow; 7102 return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); 7103 } 7104 7105 7106 //----------------------------------------------------------------------------- 7107 // [SECTION] ERROR CHECKING 7108 //----------------------------------------------------------------------------- 7109 7110 // Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui. 7111 // Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit 7112 // If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. your user code 7113 // may see different structures than what imgui.cpp sees, which is problematic. 7114 // We usually require settings to be in imconfig.h to make sure that they are accessible to all compilation units involved with Dear ImGui. 7115 bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx) 7116 { 7117 bool error = false; 7118 if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); } 7119 if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } 7120 if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } 7121 if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } 7122 if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } 7123 if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } 7124 if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); } 7125 return !error; 7126 } 7127 7128 static void ImGui::ErrorCheckNewFrameSanityChecks() 7129 { 7130 ImGuiContext& g = *GImGui; 7131 7132 // Check user IM_ASSERT macro 7133 // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined! 7134 // If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block. 7135 // This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.) 7136 // #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong! 7137 // #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct! 7138 if (true) IM_ASSERT(1); else IM_ASSERT(0); 7139 7140 // Check user data 7141 // (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) 7142 IM_ASSERT(g.Initialized); 7143 IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); 7144 IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); 7145 IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); 7146 IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); 7147 IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); 7148 IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); 7149 IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); 7150 IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations 7151 IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); 7152 IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); 7153 for (int n = 0; n < ImGuiKey_COUNT; n++) 7154 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)"); 7155 7156 // 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 added in 1.60 WIP) 7157 if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) 7158 IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); 7159 7160 // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. 7161 if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) 7162 g.IO.ConfigWindowsResizeFromEdges = false; 7163 } 7164 7165 static void ImGui::ErrorCheckEndFrameSanityChecks() 7166 { 7167 ImGuiContext& g = *GImGui; 7168 7169 // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() 7170 // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). 7171 // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will 7172 // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. 7173 // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), 7174 // while still correctly asserting on mid-frame key press events. 7175 const ImGuiKeyModFlags key_mod_flags = GetMergedKeyModFlags(); 7176 IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); 7177 IM_UNUSED(key_mod_flags); 7178 7179 // Recover from errors 7180 //ErrorCheckEndFrameRecover(); 7181 7182 // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you 7183 // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). 7184 if (g.CurrentWindowStack.Size != 1) 7185 { 7186 if (g.CurrentWindowStack.Size > 1) 7187 { 7188 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); 7189 while (g.CurrentWindowStack.Size > 1) 7190 End(); 7191 } 7192 else 7193 { 7194 IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); 7195 } 7196 } 7197 7198 IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); 7199 } 7200 7201 // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. 7202 // Must be called during or before EndFrame(). 7203 // This is generally flawed as we are not necessarily End/Popping things in the right order. 7204 // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. 7205 // FIXME: Can't recover from interleaved BeginTabBar/Begin 7206 void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data) 7207 { 7208 // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" 7209 ImGuiContext& g = *GImGui; 7210 while (g.CurrentWindowStack.Size > 0) 7211 { 7212 while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) 7213 { 7214 if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); 7215 EndTable(); 7216 } 7217 ImGuiWindow* window = g.CurrentWindow; 7218 IM_ASSERT(window != NULL); 7219 while (g.CurrentTabBar != NULL) //-V1044 7220 { 7221 if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); 7222 EndTabBar(); 7223 } 7224 while (window->DC.TreeDepth > 0) 7225 { 7226 if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); 7227 TreePop(); 7228 } 7229 while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) 7230 { 7231 if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); 7232 EndGroup(); 7233 } 7234 while (window->IDStack.Size > 1) 7235 { 7236 if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); 7237 PopID(); 7238 } 7239 while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) 7240 { 7241 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); 7242 PopStyleColor(); 7243 } 7244 while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) 7245 { 7246 if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); 7247 PopStyleVar(); 7248 } 7249 while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) 7250 { 7251 if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); 7252 PopFocusScope(); 7253 } 7254 if (g.CurrentWindowStack.Size == 1) 7255 { 7256 IM_ASSERT(g.CurrentWindow->IsFallbackWindow); 7257 break; 7258 } 7259 IM_ASSERT(window == g.CurrentWindow); 7260 if (window->Flags & ImGuiWindowFlags_ChildWindow) 7261 { 7262 if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name); 7263 EndChild(); 7264 } 7265 else 7266 { 7267 if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name); 7268 End(); 7269 } 7270 } 7271 } 7272 7273 // Save current stack sizes for later compare 7274 void ImGuiStackSizes::SetToCurrentState() 7275 { 7276 ImGuiContext& g = *GImGui; 7277 ImGuiWindow* window = g.CurrentWindow; 7278 SizeOfIDStack = (short)window->IDStack.Size; 7279 SizeOfColorStack = (short)g.ColorStack.Size; 7280 SizeOfStyleVarStack = (short)g.StyleVarStack.Size; 7281 SizeOfFontStack = (short)g.FontStack.Size; 7282 SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; 7283 SizeOfGroupStack = (short)g.GroupStack.Size; 7284 SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; 7285 } 7286 7287 // Compare to detect usage errors 7288 void ImGuiStackSizes::CompareWithCurrentState() 7289 { 7290 ImGuiContext& g = *GImGui; 7291 ImGuiWindow* window = g.CurrentWindow; 7292 IM_UNUSED(window); 7293 7294 // Window stacks 7295 // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) 7296 IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!"); 7297 7298 // Global stacks 7299 // 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. 7300 IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); 7301 IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); 7302 IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); 7303 IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); 7304 IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); 7305 IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!"); 7306 } 7307 7308 7309 //----------------------------------------------------------------------------- 7310 // [SECTION] LAYOUT 7311 //----------------------------------------------------------------------------- 7312 // - ItemSize() 7313 // - ItemAdd() 7314 // - SameLine() 7315 // - GetCursorScreenPos() 7316 // - SetCursorScreenPos() 7317 // - GetCursorPos(), GetCursorPosX(), GetCursorPosY() 7318 // - SetCursorPos(), SetCursorPosX(), SetCursorPosY() 7319 // - GetCursorStartPos() 7320 // - Indent() 7321 // - Unindent() 7322 // - SetNextItemWidth() 7323 // - PushItemWidth() 7324 // - PushMultiItemsWidths() 7325 // - PopItemWidth() 7326 // - CalcItemWidth() 7327 // - CalcItemSize() 7328 // - GetTextLineHeight() 7329 // - GetTextLineHeightWithSpacing() 7330 // - GetFrameHeight() 7331 // - GetFrameHeightWithSpacing() 7332 // - GetContentRegionMax() 7333 // - GetContentRegionMaxAbs() [Internal] 7334 // - GetContentRegionAvail(), 7335 // - GetWindowContentRegionMin(), GetWindowContentRegionMax() 7336 // - GetWindowContentRegionWidth() 7337 // - BeginGroup() 7338 // - EndGroup() 7339 // Also see in imgui_widgets: tab bars, columns. 7340 //----------------------------------------------------------------------------- 7341 7342 // Advance cursor given item size for layout. 7343 // Register minimum needed size so it can extend the bounding box used for auto-fit calculation. 7344 // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. 7345 void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) 7346 { 7347 ImGuiContext& g = *GImGui; 7348 ImGuiWindow* window = g.CurrentWindow; 7349 if (window->SkipItems) 7350 return; 7351 7352 // We increase the height in this function to accommodate for baseline offset. 7353 // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor, 7354 // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect. 7355 const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f; 7356 const float line_height = ImMax(window->DC.CurrLineSize.y, size.y + offset_to_match_baseline_y); 7357 7358 // Always align ourselves on pixel boundaries 7359 //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] 7360 window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; 7361 window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y; 7362 window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line 7363 window->DC.CursorPos.y = IM_FLOOR(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); // Next line 7364 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); 7365 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); 7366 //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] 7367 7368 window->DC.PrevLineSize.y = line_height; 7369 window->DC.CurrLineSize.y = 0.0f; 7370 window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); 7371 window->DC.CurrLineTextBaseOffset = 0.0f; 7372 7373 // Horizontal layout mode 7374 if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) 7375 SameLine(); 7376 } 7377 7378 void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) 7379 { 7380 ItemSize(bb.GetSize(), text_baseline_y); 7381 } 7382 7383 // Declare item bounding box for clipping and interaction. 7384 // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface 7385 // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. 7386 bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemAddFlags flags) 7387 { 7388 ImGuiContext& g = *GImGui; 7389 ImGuiWindow* window = g.CurrentWindow; 7390 7391 if (id != 0) 7392 { 7393 // Navigation processing runs prior to clipping early-out 7394 // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget 7395 // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests 7396 // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of 7397 // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. 7398 // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able 7399 // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). 7400 // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. 7401 // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. 7402 window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); 7403 if (g.NavId == id || g.NavAnyRequest) 7404 if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) 7405 if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) 7406 NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); 7407 7408 // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() 7409 #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 7410 if (id == g.DebugItemPickerBreakId) 7411 { 7412 IM_DEBUG_BREAK(); 7413 g.DebugItemPickerBreakId = 0; 7414 } 7415 #endif 7416 } 7417 7418 // Equivalent to calling SetLastItemData() 7419 window->DC.LastItemId = id; 7420 window->DC.LastItemRect = bb; 7421 window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; 7422 g.NextItemData.Flags = ImGuiNextItemDataFlags_None; 7423 7424 #ifdef IMGUI_ENABLE_TEST_ENGINE 7425 if (id != 0) 7426 IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id); 7427 #endif 7428 7429 // Clipping test 7430 const bool is_clipped = IsClippedEx(bb, id, false); 7431 if (is_clipped) 7432 return false; 7433 //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] 7434 7435 // Tab stop handling (previously was using internal ItemFocusable() api) 7436 // FIXME-NAV: We would now want to move this above the clipping test, but this would require being able to scroll and currently this would mean an extra frame. (#4079, #343) 7437 if (flags & ImGuiItemAddFlags_Focusable) 7438 ItemFocusable(window, id); 7439 7440 // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) 7441 if (IsMouseHoveringRect(bb.Min, bb.Max)) 7442 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; 7443 return true; 7444 } 7445 7446 // Gets back to previous line and continue with horizontal layout 7447 // offset_from_start_x == 0 : follow right after previous item 7448 // offset_from_start_x != 0 : align to specified x position (relative to window/group left) 7449 // spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 7450 // spacing_w >= 0 : enforce spacing amount 7451 void ImGui::SameLine(float offset_from_start_x, float spacing_w) 7452 { 7453 ImGuiWindow* window = GetCurrentWindow(); 7454 if (window->SkipItems) 7455 return; 7456 7457 ImGuiContext& g = *GImGui; 7458 if (offset_from_start_x != 0.0f) 7459 { 7460 if (spacing_w < 0.0f) spacing_w = 0.0f; 7461 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x; 7462 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 7463 } 7464 else 7465 { 7466 if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; 7467 window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; 7468 window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; 7469 } 7470 window->DC.CurrLineSize = window->DC.PrevLineSize; 7471 window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; 7472 } 7473 7474 ImVec2 ImGui::GetCursorScreenPos() 7475 { 7476 ImGuiWindow* window = GetCurrentWindowRead(); 7477 return window->DC.CursorPos; 7478 } 7479 7480 void ImGui::SetCursorScreenPos(const ImVec2& pos) 7481 { 7482 ImGuiWindow* window = GetCurrentWindow(); 7483 window->DC.CursorPos = pos; 7484 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 7485 } 7486 7487 // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. 7488 // 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'. 7489 ImVec2 ImGui::GetCursorPos() 7490 { 7491 ImGuiWindow* window = GetCurrentWindowRead(); 7492 return window->DC.CursorPos - window->Pos + window->Scroll; 7493 } 7494 7495 float ImGui::GetCursorPosX() 7496 { 7497 ImGuiWindow* window = GetCurrentWindowRead(); 7498 return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; 7499 } 7500 7501 float ImGui::GetCursorPosY() 7502 { 7503 ImGuiWindow* window = GetCurrentWindowRead(); 7504 return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; 7505 } 7506 7507 void ImGui::SetCursorPos(const ImVec2& local_pos) 7508 { 7509 ImGuiWindow* window = GetCurrentWindow(); 7510 window->DC.CursorPos = window->Pos - window->Scroll + local_pos; 7511 window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); 7512 } 7513 7514 void ImGui::SetCursorPosX(float x) 7515 { 7516 ImGuiWindow* window = GetCurrentWindow(); 7517 window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; 7518 window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); 7519 } 7520 7521 void ImGui::SetCursorPosY(float y) 7522 { 7523 ImGuiWindow* window = GetCurrentWindow(); 7524 window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; 7525 window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); 7526 } 7527 7528 ImVec2 ImGui::GetCursorStartPos() 7529 { 7530 ImGuiWindow* window = GetCurrentWindowRead(); 7531 return window->DC.CursorStartPos - window->Pos; 7532 } 7533 7534 void ImGui::Indent(float indent_w) 7535 { 7536 ImGuiContext& g = *GImGui; 7537 ImGuiWindow* window = GetCurrentWindow(); 7538 window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 7539 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; 7540 } 7541 7542 void ImGui::Unindent(float indent_w) 7543 { 7544 ImGuiContext& g = *GImGui; 7545 ImGuiWindow* window = GetCurrentWindow(); 7546 window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; 7547 window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; 7548 } 7549 7550 // Affect large frame+labels widgets only. 7551 void ImGui::SetNextItemWidth(float item_width) 7552 { 7553 ImGuiContext& g = *GImGui; 7554 g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth; 7555 g.NextItemData.Width = item_width; 7556 } 7557 7558 // FIXME: Remove the == 0.0f behavior? 7559 void ImGui::PushItemWidth(float item_width) 7560 { 7561 ImGuiContext& g = *GImGui; 7562 ImGuiWindow* window = g.CurrentWindow; 7563 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width 7564 window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); 7565 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; 7566 } 7567 7568 void ImGui::PushMultiItemsWidths(int components, float w_full) 7569 { 7570 ImGuiContext& g = *GImGui; 7571 ImGuiWindow* window = g.CurrentWindow; 7572 const ImGuiStyle& style = g.Style; 7573 const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); 7574 const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); 7575 window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width 7576 window->DC.ItemWidthStack.push_back(w_item_last); 7577 for (int i = 0; i < components - 2; i++) 7578 window->DC.ItemWidthStack.push_back(w_item_one); 7579 window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one; 7580 g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; 7581 } 7582 7583 void ImGui::PopItemWidth() 7584 { 7585 ImGuiWindow* window = GetCurrentWindow(); 7586 window->DC.ItemWidth = window->DC.ItemWidthStack.back(); 7587 window->DC.ItemWidthStack.pop_back(); 7588 } 7589 7590 // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(). 7591 // The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags() 7592 float ImGui::CalcItemWidth() 7593 { 7594 ImGuiContext& g = *GImGui; 7595 ImGuiWindow* window = g.CurrentWindow; 7596 float w; 7597 if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) 7598 w = g.NextItemData.Width; 7599 else 7600 w = window->DC.ItemWidth; 7601 if (w < 0.0f) 7602 { 7603 float region_max_x = GetContentRegionMaxAbs().x; 7604 w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); 7605 } 7606 w = IM_FLOOR(w); 7607 return w; 7608 } 7609 7610 // [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth(). 7611 // Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical. 7612 // Note that only CalcItemWidth() is publicly exposed. 7613 // The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable) 7614 ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h) 7615 { 7616 ImGuiWindow* window = GImGui->CurrentWindow; 7617 7618 ImVec2 region_max; 7619 if (size.x < 0.0f || size.y < 0.0f) 7620 region_max = GetContentRegionMaxAbs(); 7621 7622 if (size.x == 0.0f) 7623 size.x = default_w; 7624 else if (size.x < 0.0f) 7625 size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x); 7626 7627 if (size.y == 0.0f) 7628 size.y = default_h; 7629 else if (size.y < 0.0f) 7630 size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y); 7631 7632 return size; 7633 } 7634 7635 float ImGui::GetTextLineHeight() 7636 { 7637 ImGuiContext& g = *GImGui; 7638 return g.FontSize; 7639 } 7640 7641 float ImGui::GetTextLineHeightWithSpacing() 7642 { 7643 ImGuiContext& g = *GImGui; 7644 return g.FontSize + g.Style.ItemSpacing.y; 7645 } 7646 7647 float ImGui::GetFrameHeight() 7648 { 7649 ImGuiContext& g = *GImGui; 7650 return g.FontSize + g.Style.FramePadding.y * 2.0f; 7651 } 7652 7653 float ImGui::GetFrameHeightWithSpacing() 7654 { 7655 ImGuiContext& g = *GImGui; 7656 return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; 7657 } 7658 7659 // 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! 7660 7661 // FIXME: This is in window space (not screen space!). 7662 ImVec2 ImGui::GetContentRegionMax() 7663 { 7664 ImGuiContext& g = *GImGui; 7665 ImGuiWindow* window = g.CurrentWindow; 7666 ImVec2 mx = window->ContentRegionRect.Max - window->Pos; 7667 if (window->DC.CurrentColumns || g.CurrentTable) 7668 mx.x = window->WorkRect.Max.x - window->Pos.x; 7669 return mx; 7670 } 7671 7672 // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. 7673 ImVec2 ImGui::GetContentRegionMaxAbs() 7674 { 7675 ImGuiContext& g = *GImGui; 7676 ImGuiWindow* window = g.CurrentWindow; 7677 ImVec2 mx = window->ContentRegionRect.Max; 7678 if (window->DC.CurrentColumns || g.CurrentTable) 7679 mx.x = window->WorkRect.Max.x; 7680 return mx; 7681 } 7682 7683 ImVec2 ImGui::GetContentRegionAvail() 7684 { 7685 ImGuiWindow* window = GImGui->CurrentWindow; 7686 return GetContentRegionMaxAbs() - window->DC.CursorPos; 7687 } 7688 7689 // In window space (not screen space!) 7690 ImVec2 ImGui::GetWindowContentRegionMin() 7691 { 7692 ImGuiWindow* window = GImGui->CurrentWindow; 7693 return window->ContentRegionRect.Min - window->Pos; 7694 } 7695 7696 ImVec2 ImGui::GetWindowContentRegionMax() 7697 { 7698 ImGuiWindow* window = GImGui->CurrentWindow; 7699 return window->ContentRegionRect.Max - window->Pos; 7700 } 7701 7702 float ImGui::GetWindowContentRegionWidth() 7703 { 7704 ImGuiWindow* window = GImGui->CurrentWindow; 7705 return window->ContentRegionRect.GetWidth(); 7706 } 7707 7708 // 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.) 7709 // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. 7710 void ImGui::BeginGroup() 7711 { 7712 ImGuiContext& g = *GImGui; 7713 ImGuiWindow* window = g.CurrentWindow; 7714 7715 g.GroupStack.resize(g.GroupStack.Size + 1); 7716 ImGuiGroupData& group_data = g.GroupStack.back(); 7717 group_data.WindowID = window->ID; 7718 group_data.BackupCursorPos = window->DC.CursorPos; 7719 group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; 7720 group_data.BackupIndent = window->DC.Indent; 7721 group_data.BackupGroupOffset = window->DC.GroupOffset; 7722 group_data.BackupCurrLineSize = window->DC.CurrLineSize; 7723 group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; 7724 group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; 7725 group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; 7726 group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; 7727 group_data.EmitItem = true; 7728 7729 window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x; 7730 window->DC.Indent = window->DC.GroupOffset; 7731 window->DC.CursorMaxPos = window->DC.CursorPos; 7732 window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); 7733 if (g.LogEnabled) 7734 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return 7735 } 7736 7737 void ImGui::EndGroup() 7738 { 7739 ImGuiContext& g = *GImGui; 7740 ImGuiWindow* window = g.CurrentWindow; 7741 IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls 7742 7743 ImGuiGroupData& group_data = g.GroupStack.back(); 7744 IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window? 7745 7746 ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); 7747 7748 window->DC.CursorPos = group_data.BackupCursorPos; 7749 window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); 7750 window->DC.Indent = group_data.BackupIndent; 7751 window->DC.GroupOffset = group_data.BackupGroupOffset; 7752 window->DC.CurrLineSize = group_data.BackupCurrLineSize; 7753 window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; 7754 if (g.LogEnabled) 7755 g.LogLinePosY = -FLT_MAX; // To enforce a carriage return 7756 7757 if (!group_data.EmitItem) 7758 { 7759 g.GroupStack.pop_back(); 7760 return; 7761 } 7762 7763 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. 7764 ItemSize(group_bb.GetSize()); 7765 ItemAdd(group_bb, 0); 7766 7767 // 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. 7768 // 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. 7769 // Also if you grep for LastItemId you'll notice it is only used in that context. 7770 // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) 7771 const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; 7772 const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); 7773 if (group_contains_curr_active_id) 7774 window->DC.LastItemId = g.ActiveId; 7775 else if (group_contains_prev_active_id) 7776 window->DC.LastItemId = g.ActiveIdPreviousFrame; 7777 window->DC.LastItemRect = group_bb; 7778 7779 // Forward Hovered flag 7780 const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; 7781 if (group_contains_curr_hovered_id) 7782 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; 7783 7784 // Forward Edited flag 7785 if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) 7786 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; 7787 7788 // Forward Deactivated flag 7789 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; 7790 if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) 7791 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; 7792 7793 g.GroupStack.pop_back(); 7794 //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] 7795 } 7796 7797 7798 //----------------------------------------------------------------------------- 7799 // [SECTION] SCROLLING 7800 //----------------------------------------------------------------------------- 7801 7802 // Helper to snap on edges when aiming at an item very close to the edge, 7803 // So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling. 7804 // When we refactor the scrolling API this may be configurable with a flag? 7805 // Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default. 7806 static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio) 7807 { 7808 if (target <= snap_min + snap_threshold) 7809 return ImLerp(snap_min, target, center_ratio); 7810 if (target >= snap_max - snap_threshold) 7811 return ImLerp(target, snap_max, center_ratio); 7812 return target; 7813 } 7814 7815 static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) 7816 { 7817 ImVec2 scroll = window->Scroll; 7818 if (window->ScrollTarget.x < FLT_MAX) 7819 { 7820 float decoration_total_width = window->ScrollbarSizes.x; 7821 float center_x_ratio = window->ScrollTargetCenterRatio.x; 7822 float scroll_target_x = window->ScrollTarget.x; 7823 if (window->ScrollTargetEdgeSnapDist.x > 0.0f) 7824 { 7825 float snap_x_min = 0.0f; 7826 float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; 7827 scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); 7828 } 7829 scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); 7830 } 7831 if (window->ScrollTarget.y < FLT_MAX) 7832 { 7833 float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; 7834 float center_y_ratio = window->ScrollTargetCenterRatio.y; 7835 float scroll_target_y = window->ScrollTarget.y; 7836 if (window->ScrollTargetEdgeSnapDist.y > 0.0f) 7837 { 7838 float snap_y_min = 0.0f; 7839 float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; 7840 scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); 7841 } 7842 scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); 7843 } 7844 scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); 7845 scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); 7846 if (!window->Collapsed && !window->SkipItems) 7847 { 7848 scroll.x = ImMin(scroll.x, window->ScrollMax.x); 7849 scroll.y = ImMin(scroll.y, window->ScrollMax.y); 7850 } 7851 return scroll; 7852 } 7853 7854 // Scroll to keep newly navigated item fully into view 7855 ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) 7856 { 7857 ImGuiContext& g = *GImGui; 7858 ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); 7859 //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] 7860 7861 ImVec2 delta_scroll; 7862 if (!window_rect.Contains(item_rect)) 7863 { 7864 if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) 7865 SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); 7866 else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) 7867 SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); 7868 if (item_rect.Min.y < window_rect.Min.y) 7869 SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); 7870 else if (item_rect.Max.y >= window_rect.Max.y) 7871 SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); 7872 7873 ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); 7874 delta_scroll = next_scroll - window->Scroll; 7875 } 7876 7877 // Also scroll parent window to keep us into view if necessary 7878 if (window->Flags & ImGuiWindowFlags_ChildWindow) 7879 delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); 7880 7881 return delta_scroll; 7882 } 7883 7884 float ImGui::GetScrollX() 7885 { 7886 ImGuiWindow* window = GImGui->CurrentWindow; 7887 return window->Scroll.x; 7888 } 7889 7890 float ImGui::GetScrollY() 7891 { 7892 ImGuiWindow* window = GImGui->CurrentWindow; 7893 return window->Scroll.y; 7894 } 7895 7896 float ImGui::GetScrollMaxX() 7897 { 7898 ImGuiWindow* window = GImGui->CurrentWindow; 7899 return window->ScrollMax.x; 7900 } 7901 7902 float ImGui::GetScrollMaxY() 7903 { 7904 ImGuiWindow* window = GImGui->CurrentWindow; 7905 return window->ScrollMax.y; 7906 } 7907 7908 void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x) 7909 { 7910 window->ScrollTarget.x = scroll_x; 7911 window->ScrollTargetCenterRatio.x = 0.0f; 7912 window->ScrollTargetEdgeSnapDist.x = 0.0f; 7913 } 7914 7915 void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y) 7916 { 7917 window->ScrollTarget.y = scroll_y; 7918 window->ScrollTargetCenterRatio.y = 0.0f; 7919 window->ScrollTargetEdgeSnapDist.y = 0.0f; 7920 } 7921 7922 void ImGui::SetScrollX(float scroll_x) 7923 { 7924 ImGuiContext& g = *GImGui; 7925 SetScrollX(g.CurrentWindow, scroll_x); 7926 } 7927 7928 void ImGui::SetScrollY(float scroll_y) 7929 { 7930 ImGuiContext& g = *GImGui; 7931 SetScrollY(g.CurrentWindow, scroll_y); 7932 } 7933 7934 // Note that a local position will vary depending on initial scroll value, 7935 // This is a little bit confusing so bear with us: 7936 // - local_pos = (absolution_pos - window->Pos) 7937 // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window, 7938 // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area. 7939 // - They mostly exists because of legacy API. 7940 // Following the rules above, when trying to work with scrolling code, consider that: 7941 // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect! 7942 // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense 7943 // We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size 7944 void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) 7945 { 7946 IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); 7947 window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset 7948 window->ScrollTargetCenterRatio.x = center_x_ratio; 7949 window->ScrollTargetEdgeSnapDist.x = 0.0f; 7950 } 7951 7952 void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) 7953 { 7954 IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); 7955 const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; 7956 local_y -= decoration_up_height; 7957 window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset 7958 window->ScrollTargetCenterRatio.y = center_y_ratio; 7959 window->ScrollTargetEdgeSnapDist.y = 0.0f; 7960 } 7961 7962 void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) 7963 { 7964 ImGuiContext& g = *GImGui; 7965 SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio); 7966 } 7967 7968 void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) 7969 { 7970 ImGuiContext& g = *GImGui; 7971 SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio); 7972 } 7973 7974 // center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item. 7975 void ImGui::SetScrollHereX(float center_x_ratio) 7976 { 7977 ImGuiContext& g = *GImGui; 7978 ImGuiWindow* window = g.CurrentWindow; 7979 float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); 7980 float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); 7981 SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos 7982 7983 // Tweak: snap on edges when aiming at an item very close to the edge 7984 window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x); 7985 } 7986 7987 // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. 7988 void ImGui::SetScrollHereY(float center_y_ratio) 7989 { 7990 ImGuiContext& g = *GImGui; 7991 ImGuiWindow* window = g.CurrentWindow; 7992 float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y); 7993 float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio); 7994 SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos 7995 7996 // Tweak: snap on edges when aiming at an item very close to the edge 7997 window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y); 7998 } 7999 8000 //----------------------------------------------------------------------------- 8001 // [SECTION] TOOLTIPS 8002 //----------------------------------------------------------------------------- 8003 8004 void ImGui::BeginTooltip() 8005 { 8006 BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); 8007 } 8008 8009 void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) 8010 { 8011 ImGuiContext& g = *GImGui; 8012 8013 if (g.DragDropWithinSource || g.DragDropWithinTarget) 8014 { 8015 // 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) 8016 // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. 8017 // 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. 8018 //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; 8019 ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); 8020 SetNextWindowPos(tooltip_pos); 8021 SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); 8022 //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( 8023 tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; 8024 } 8025 8026 char window_name[16]; 8027 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); 8028 if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) 8029 if (ImGuiWindow* window = FindWindowByName(window_name)) 8030 if (window->Active) 8031 { 8032 // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. 8033 window->Hidden = true; 8034 window->HiddenFramesCanSkipItems = 1; // FIXME: This may not be necessary? 8035 ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); 8036 } 8037 ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; 8038 Begin(window_name, NULL, flags | extra_flags); 8039 } 8040 8041 void ImGui::EndTooltip() 8042 { 8043 IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls 8044 End(); 8045 } 8046 8047 void ImGui::SetTooltipV(const char* fmt, va_list args) 8048 { 8049 BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); 8050 TextV(fmt, args); 8051 EndTooltip(); 8052 } 8053 8054 void ImGui::SetTooltip(const char* fmt, ...) 8055 { 8056 va_list args; 8057 va_start(args, fmt); 8058 SetTooltipV(fmt, args); 8059 va_end(args); 8060 } 8061 8062 //----------------------------------------------------------------------------- 8063 // [SECTION] POPUPS 8064 //----------------------------------------------------------------------------- 8065 8066 // Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel 8067 bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags) 8068 { 8069 ImGuiContext& g = *GImGui; 8070 if (popup_flags & ImGuiPopupFlags_AnyPopupId) 8071 { 8072 // Return true if any popup is open at the current BeginPopup() level of the popup stack 8073 // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level. 8074 IM_ASSERT(id == 0); 8075 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) 8076 return g.OpenPopupStack.Size > 0; 8077 else 8078 return g.OpenPopupStack.Size > g.BeginPopupStack.Size; 8079 } 8080 else 8081 { 8082 if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) 8083 { 8084 // Return true if the popup is open anywhere in the popup stack 8085 for (int n = 0; n < g.OpenPopupStack.Size; n++) 8086 if (g.OpenPopupStack[n].PopupId == id) 8087 return true; 8088 return false; 8089 } 8090 else 8091 { 8092 // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query) 8093 return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; 8094 } 8095 } 8096 } 8097 8098 bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags) 8099 { 8100 ImGuiContext& g = *GImGui; 8101 ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id); 8102 if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0) 8103 IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally 8104 return IsPopupOpen(id, popup_flags); 8105 } 8106 8107 ImGuiWindow* ImGui::GetTopMostPopupModal() 8108 { 8109 ImGuiContext& g = *GImGui; 8110 for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) 8111 if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) 8112 if (popup->Flags & ImGuiWindowFlags_Modal) 8113 return popup; 8114 return NULL; 8115 } 8116 8117 void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) 8118 { 8119 ImGuiContext& g = *GImGui; 8120 OpenPopupEx(g.CurrentWindow->GetID(str_id), popup_flags); 8121 } 8122 8123 void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) 8124 { 8125 OpenPopupEx(id, popup_flags); 8126 } 8127 8128 // Mark popup as open (toggle toward open state). 8129 // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. 8130 // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). 8131 // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) 8132 void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) 8133 { 8134 ImGuiContext& g = *GImGui; 8135 ImGuiWindow* parent_window = g.CurrentWindow; 8136 const int current_stack_size = g.BeginPopupStack.Size; 8137 8138 if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup) 8139 if (IsPopupOpen(0u, ImGuiPopupFlags_AnyPopupId)) 8140 return; 8141 8142 ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. 8143 popup_ref.PopupId = id; 8144 popup_ref.Window = NULL; 8145 popup_ref.SourceWindow = g.NavWindow; 8146 popup_ref.OpenFrameCount = g.FrameCount; 8147 popup_ref.OpenParentId = parent_window->IDStack.back(); 8148 popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); 8149 popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; 8150 8151 IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id); 8152 if (g.OpenPopupStack.Size < current_stack_size + 1) 8153 { 8154 g.OpenPopupStack.push_back(popup_ref); 8155 } 8156 else 8157 { 8158 // 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 8159 // 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 8160 // 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. 8161 if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) 8162 { 8163 g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; 8164 } 8165 else 8166 { 8167 // Close child popups if any, then flag popup for open/reopen 8168 ClosePopupToLevel(current_stack_size, false); 8169 g.OpenPopupStack.push_back(popup_ref); 8170 } 8171 8172 // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). 8173 // This is equivalent to what ClosePopupToLevel() does. 8174 //if (g.OpenPopupStack[current_stack_size].PopupId == id) 8175 // FocusWindow(parent_window); 8176 } 8177 } 8178 8179 // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. 8180 // This function closes any popups that are over 'ref_window'. 8181 void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup) 8182 { 8183 ImGuiContext& g = *GImGui; 8184 if (g.OpenPopupStack.Size == 0) 8185 return; 8186 8187 // Don't close our own child popup windows. 8188 int popup_count_to_keep = 0; 8189 if (ref_window) 8190 { 8191 // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) 8192 for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) 8193 { 8194 ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep]; 8195 if (!popup.Window) 8196 continue; 8197 IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); 8198 if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) 8199 continue; 8200 8201 // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow) 8202 // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3: 8203 // Window -> Popup1 -> Popup2 -> Popup3 8204 // - Each popups may contain child windows, which is why we compare ->RootWindow! 8205 // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child 8206 bool ref_window_is_descendent_of_popup = false; 8207 for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) 8208 if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) 8209 if (popup_window->RootWindow == ref_window->RootWindow) 8210 { 8211 ref_window_is_descendent_of_popup = true; 8212 break; 8213 } 8214 if (!ref_window_is_descendent_of_popup) 8215 break; 8216 } 8217 } 8218 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 8219 { 8220 IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); 8221 ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); 8222 } 8223 } 8224 8225 void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) 8226 { 8227 ImGuiContext& g = *GImGui; 8228 IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); 8229 IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); 8230 8231 // Trim open popup stack 8232 ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; 8233 ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; 8234 g.OpenPopupStack.resize(remaining); 8235 8236 if (restore_focus_to_window_under_popup) 8237 { 8238 if (focus_window && !focus_window->WasActive && popup_window) 8239 { 8240 // Fallback 8241 FocusTopMostWindowUnderOne(popup_window, NULL); 8242 } 8243 else 8244 { 8245 if (g.NavLayer == ImGuiNavLayer_Main && focus_window) 8246 focus_window = NavRestoreLastChildNavWindow(focus_window); 8247 FocusWindow(focus_window); 8248 } 8249 } 8250 } 8251 8252 // Close the popup we have begin-ed into. 8253 void ImGui::CloseCurrentPopup() 8254 { 8255 ImGuiContext& g = *GImGui; 8256 int popup_idx = g.BeginPopupStack.Size - 1; 8257 if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) 8258 return; 8259 8260 // Closing a menu closes its top-most parent popup (unless a modal) 8261 while (popup_idx > 0) 8262 { 8263 ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window; 8264 ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; 8265 bool close_parent = false; 8266 if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) 8267 if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) 8268 close_parent = true; 8269 if (!close_parent) 8270 break; 8271 popup_idx--; 8272 } 8273 IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); 8274 ClosePopupToLevel(popup_idx, true); 8275 8276 // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. 8277 // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. 8278 // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic. 8279 if (ImGuiWindow* window = g.NavWindow) 8280 window->DC.NavHideHighlightOneFrame = true; 8281 } 8282 8283 // Attention! BeginPopup() adds default flags which BeginPopupEx()! 8284 bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) 8285 { 8286 ImGuiContext& g = *GImGui; 8287 if (!IsPopupOpen(id, ImGuiPopupFlags_None)) 8288 { 8289 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 8290 return false; 8291 } 8292 8293 char name[20]; 8294 if (flags & ImGuiWindowFlags_ChildMenu) 8295 ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth 8296 else 8297 ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame 8298 8299 flags |= ImGuiWindowFlags_Popup; 8300 bool is_open = Begin(name, NULL, flags); 8301 if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) 8302 EndPopup(); 8303 8304 return is_open; 8305 } 8306 8307 bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) 8308 { 8309 ImGuiContext& g = *GImGui; 8310 if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance 8311 { 8312 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 8313 return false; 8314 } 8315 flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; 8316 return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); 8317 } 8318 8319 // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. 8320 // 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. 8321 bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) 8322 { 8323 ImGuiContext& g = *GImGui; 8324 ImGuiWindow* window = g.CurrentWindow; 8325 const ImGuiID id = window->GetID(name); 8326 if (!IsPopupOpen(id, ImGuiPopupFlags_None)) 8327 { 8328 g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values 8329 return false; 8330 } 8331 8332 // Center modal windows by default for increased visibility 8333 // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) 8334 // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. 8335 if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) 8336 { 8337 const ImGuiViewport* viewport = GetMainViewport(); 8338 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); 8339 } 8340 8341 flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse; 8342 const bool is_open = Begin(name, p_open, flags); 8343 if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) 8344 { 8345 EndPopup(); 8346 if (is_open) 8347 ClosePopupToLevel(g.BeginPopupStack.Size, true); 8348 return false; 8349 } 8350 return is_open; 8351 } 8352 8353 void ImGui::EndPopup() 8354 { 8355 ImGuiContext& g = *GImGui; 8356 ImGuiWindow* window = g.CurrentWindow; 8357 IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls 8358 IM_ASSERT(g.BeginPopupStack.Size > 0); 8359 8360 // Make all menus and popups wrap around for now, may need to expose that policy. 8361 if (g.NavWindow == window) 8362 NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); 8363 8364 // Child-popups don't need to be laid out 8365 IM_ASSERT(g.WithinEndChild == false); 8366 if (window->Flags & ImGuiWindowFlags_ChildWindow) 8367 g.WithinEndChild = true; 8368 End(); 8369 g.WithinEndChild = false; 8370 } 8371 8372 // Helper to open a popup if mouse button is released over the item 8373 // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() 8374 void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) 8375 { 8376 ImGuiWindow* window = GImGui->CurrentWindow; 8377 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8378 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 8379 { 8380 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! 8381 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 8382 OpenPopupEx(id, popup_flags); 8383 } 8384 } 8385 8386 // This is a helper to handle the simplest case of associating one named popup to one given widget. 8387 // - To create a popup associated to the last item, you generally want to pass a NULL value to str_id. 8388 // - To create a popup with a specific identifier, pass it in str_id. 8389 // - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call. 8390 // - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id. 8391 // - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). 8392 // This is essentially the same as: 8393 // id = str_id ? GetID(str_id) : GetItemID(); 8394 // OpenPopupOnItemClick(str_id); 8395 // return BeginPopup(id); 8396 // Which is essentially the same as: 8397 // id = str_id ? GetID(str_id) : GetItemID(); 8398 // if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right)) 8399 // OpenPopup(id); 8400 // return BeginPopup(id); 8401 // The main difference being that this is tweaked to avoid computing the ID twice. 8402 bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) 8403 { 8404 ImGuiWindow* window = GImGui->CurrentWindow; 8405 if (window->SkipItems) 8406 return false; 8407 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! 8408 IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) 8409 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8410 if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 8411 OpenPopupEx(id, popup_flags); 8412 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 8413 } 8414 8415 bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) 8416 { 8417 ImGuiWindow* window = GImGui->CurrentWindow; 8418 if (!str_id) 8419 str_id = "window_context"; 8420 ImGuiID id = window->GetID(str_id); 8421 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8422 if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) 8423 if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) 8424 OpenPopupEx(id, popup_flags); 8425 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 8426 } 8427 8428 bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) 8429 { 8430 ImGuiWindow* window = GImGui->CurrentWindow; 8431 if (!str_id) 8432 str_id = "void_context"; 8433 ImGuiID id = window->GetID(str_id); 8434 int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); 8435 if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) 8436 if (GetTopMostPopupModal() == NULL) 8437 OpenPopupEx(id, popup_flags); 8438 return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); 8439 } 8440 8441 // 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.) 8442 // 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. 8443 // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor 8444 // information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. 8445 // this allows us to have tooltips/popups displayed out of the parent viewport.) 8446 ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) 8447 { 8448 ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); 8449 //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); 8450 //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); 8451 8452 // Combo Box policy (we want a connecting edge) 8453 if (policy == ImGuiPopupPositionPolicy_ComboBox) 8454 { 8455 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; 8456 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 8457 { 8458 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 8459 if (n != -1 && dir == *last_dir) // Already tried this direction? 8460 continue; 8461 ImVec2 pos; 8462 if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) 8463 if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right 8464 if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left 8465 if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left 8466 if (!r_outer.Contains(ImRect(pos, pos + size))) 8467 continue; 8468 *last_dir = dir; 8469 return pos; 8470 } 8471 } 8472 8473 // Tooltip and Default popup policy 8474 // (Always first try the direction we used on the last frame, if any) 8475 if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default) 8476 { 8477 const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; 8478 for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) 8479 { 8480 const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; 8481 if (n != -1 && dir == *last_dir) // Already tried this direction? 8482 continue; 8483 8484 const 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); 8485 const 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); 8486 8487 // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width) 8488 if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right)) 8489 continue; 8490 if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down)) 8491 continue; 8492 8493 ImVec2 pos; 8494 pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; 8495 pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; 8496 8497 // Clamp top-left corner of popup 8498 pos.x = ImMax(pos.x, r_outer.Min.x); 8499 pos.y = ImMax(pos.y, r_outer.Min.y); 8500 8501 *last_dir = dir; 8502 return pos; 8503 } 8504 } 8505 8506 // Fallback when not enough room: 8507 *last_dir = ImGuiDir_None; 8508 8509 // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. 8510 if (policy == ImGuiPopupPositionPolicy_Tooltip) 8511 return ref_pos + ImVec2(2, 2); 8512 8513 // Otherwise try to keep within display 8514 ImVec2 pos = ref_pos; 8515 pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); 8516 pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); 8517 return pos; 8518 } 8519 8520 // Note that this is used for popups, which can overlap the non work-area of individual viewports. 8521 ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) 8522 { 8523 ImGuiContext& g = *GImGui; 8524 IM_UNUSED(window); 8525 ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect(); 8526 ImVec2 padding = g.Style.DisplaySafeAreaPadding; 8527 r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); 8528 return r_screen; 8529 } 8530 8531 ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) 8532 { 8533 ImGuiContext& g = *GImGui; 8534 8535 ImRect r_outer = GetWindowAllowedExtentRect(window); 8536 if (window->Flags & ImGuiWindowFlags_ChildMenu) 8537 { 8538 // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. 8539 // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. 8540 IM_ASSERT(g.CurrentWindow == window); 8541 ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; 8542 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). 8543 ImRect r_avoid; 8544 if (parent_window->DC.MenuBarAppending) 8545 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 8546 else 8547 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); 8548 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); 8549 } 8550 if (window->Flags & ImGuiWindowFlags_Popup) 8551 { 8552 ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); 8553 return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); 8554 } 8555 if (window->Flags & ImGuiWindowFlags_Tooltip) 8556 { 8557 // Position tooltip (always follows mouse) 8558 float sc = g.Style.MouseCursorScale; 8559 ImVec2 ref_pos = NavCalcPreferredRefPos(); 8560 ImRect r_avoid; 8561 if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) 8562 r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); 8563 else 8564 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. 8565 return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); 8566 } 8567 IM_ASSERT(0); 8568 return window->Pos; 8569 } 8570 8571 //----------------------------------------------------------------------------- 8572 // [SECTION] KEYBOARD/GAMEPAD NAVIGATION 8573 //----------------------------------------------------------------------------- 8574 8575 // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. 8576 void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) 8577 { 8578 ImGuiContext& g = *GImGui; 8579 IM_ASSERT(g.NavWindow != NULL); 8580 IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); 8581 g.NavId = id; 8582 g.NavLayer = nav_layer; 8583 g.NavFocusScopeId = focus_scope_id; 8584 g.NavWindow->NavLastIds[nav_layer] = id; 8585 g.NavWindow->NavRectRel[nav_layer] = rect_rel; 8586 //g.NavDisableHighlight = false; 8587 //g.NavDisableMouseHover = g.NavMousePosDirty = true; 8588 } 8589 8590 void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) 8591 { 8592 ImGuiContext& g = *GImGui; 8593 IM_ASSERT(id != 0); 8594 8595 // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid. 8596 // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) 8597 const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; 8598 if (g.NavWindow != window) 8599 g.NavInitRequest = false; 8600 g.NavWindow = window; 8601 g.NavId = id; 8602 g.NavLayer = nav_layer; 8603 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; 8604 window->NavLastIds[nav_layer] = id; 8605 if (window->DC.LastItemId == id) 8606 window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); 8607 8608 if (g.ActiveIdSource == ImGuiInputSource_Nav) 8609 g.NavDisableMouseHover = true; 8610 else 8611 g.NavDisableHighlight = true; 8612 } 8613 8614 ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) 8615 { 8616 if (ImFabs(dx) > ImFabs(dy)) 8617 return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; 8618 return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; 8619 } 8620 8621 static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) 8622 { 8623 if (a1 < b0) 8624 return a1 - b0; 8625 if (b1 < a0) 8626 return a0 - b1; 8627 return 0.0f; 8628 } 8629 8630 static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) 8631 { 8632 if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) 8633 { 8634 r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); 8635 r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); 8636 } 8637 else 8638 { 8639 r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); 8640 r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); 8641 } 8642 } 8643 8644 // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 8645 static bool ImGui::NavScoreItem(ImGuiNavItemData* result, ImRect cand) 8646 { 8647 ImGuiContext& g = *GImGui; 8648 ImGuiWindow* window = g.CurrentWindow; 8649 if (g.NavLayer != window->DC.NavLayerCurrent) 8650 return false; 8651 8652 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) 8653 g.NavScoringCount++; 8654 8655 // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring 8656 if (window->ParentWindow == g.NavWindow) 8657 { 8658 IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); 8659 if (!window->ClipRect.Overlaps(cand)) 8660 return false; 8661 cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window 8662 } 8663 8664 // 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) 8665 // For example, this ensure that items in one column are not reached when moving vertically from items in another column. 8666 NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); 8667 8668 // Compute distance between boxes 8669 // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. 8670 float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); 8671 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 8672 if (dby != 0.0f && dbx != 0.0f) 8673 dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); 8674 float dist_box = ImFabs(dbx) + ImFabs(dby); 8675 8676 // 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) 8677 float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); 8678 float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); 8679 float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) 8680 8681 // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance 8682 ImGuiDir quadrant; 8683 float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; 8684 if (dbx != 0.0f || dby != 0.0f) 8685 { 8686 // For non-overlapping boxes, use distance between boxes 8687 dax = dbx; 8688 day = dby; 8689 dist_axial = dist_box; 8690 quadrant = ImGetDirQuadrantFromDelta(dbx, dby); 8691 } 8692 else if (dcx != 0.0f || dcy != 0.0f) 8693 { 8694 // For overlapping boxes with different centers, use distance between centers 8695 dax = dcx; 8696 day = dcy; 8697 dist_axial = dist_center; 8698 quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); 8699 } 8700 else 8701 { 8702 // 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) 8703 quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; 8704 } 8705 8706 #if IMGUI_DEBUG_NAV_SCORING 8707 char buf[128]; 8708 if (IsMouseHoveringRect(cand.Min, cand.Max)) 8709 { 8710 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]); 8711 ImDrawList* draw_list = GetForegroundDrawList(window); 8712 draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); 8713 draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); 8714 draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); 8715 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); 8716 } 8717 else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. 8718 { 8719 if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } 8720 if (quadrant == g.NavMoveDir) 8721 { 8722 ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); 8723 ImDrawList* draw_list = GetForegroundDrawList(window); 8724 draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); 8725 draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); 8726 } 8727 } 8728 #endif 8729 8730 // Is it in the quadrant we're interesting in moving to? 8731 bool new_best = false; 8732 if (quadrant == g.NavMoveDir) 8733 { 8734 // Does it beat the current best candidate? 8735 if (dist_box < result->DistBox) 8736 { 8737 result->DistBox = dist_box; 8738 result->DistCenter = dist_center; 8739 return true; 8740 } 8741 if (dist_box == result->DistBox) 8742 { 8743 // Try using distance between center points to break ties 8744 if (dist_center < result->DistCenter) 8745 { 8746 result->DistCenter = dist_center; 8747 new_best = true; 8748 } 8749 else if (dist_center == result->DistCenter) 8750 { 8751 // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items 8752 // (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), 8753 // 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. 8754 if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance 8755 new_best = true; 8756 } 8757 } 8758 } 8759 8760 // 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 8761 // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) 8762 // 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. 8763 // 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. 8764 // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? 8765 if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match 8766 if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) 8767 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)) 8768 { 8769 result->DistAxial = dist_axial; 8770 new_best = true; 8771 } 8772 8773 return new_best; 8774 } 8775 8776 static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) 8777 { 8778 result->Window = window; 8779 result->ID = id; 8780 result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; 8781 result->RectRel = nav_bb_rel; 8782 } 8783 8784 // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) 8785 static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) 8786 { 8787 ImGuiContext& g = *GImGui; 8788 //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. 8789 // return; 8790 8791 const ImGuiItemFlags item_flags = g.CurrentItemFlags; 8792 const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); 8793 8794 // Process Init Request 8795 if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) 8796 { 8797 // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback 8798 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) 8799 { 8800 g.NavInitResultId = id; 8801 g.NavInitResultRectRel = nav_bb_rel; 8802 } 8803 if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) 8804 { 8805 g.NavInitRequest = false; // Found a match, clear request 8806 NavUpdateAnyRequestFlag(); 8807 } 8808 } 8809 8810 // Process Move Request (scoring for navigation) 8811 // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) 8812 if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) 8813 { 8814 ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 8815 #if IMGUI_DEBUG_NAV_SCORING 8816 // [DEBUG] Score all items in NavWindow at all times 8817 if (!g.NavMoveRequest) 8818 g.NavMoveDir = g.NavMoveDirLast; 8819 bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; 8820 #else 8821 bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); 8822 #endif 8823 if (new_best) 8824 NavApplyItemToResult(result, window, id, nav_bb_rel); 8825 8826 // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. 8827 const float VISIBLE_RATIO = 0.70f; 8828 if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) 8829 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) 8830 if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) 8831 NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel); 8832 } 8833 8834 // Update window-relative bounding box of navigated item 8835 if (g.NavId == id) 8836 { 8837 g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. 8838 g.NavLayer = window->DC.NavLayerCurrent; 8839 g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; 8840 g.NavIdIsAlive = true; 8841 window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) 8842 } 8843 } 8844 8845 bool ImGui::NavMoveRequestButNoResultYet() 8846 { 8847 ImGuiContext& g = *GImGui; 8848 return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; 8849 } 8850 8851 void ImGui::NavMoveRequestCancel() 8852 { 8853 ImGuiContext& g = *GImGui; 8854 g.NavMoveRequest = false; 8855 NavUpdateAnyRequestFlag(); 8856 } 8857 8858 void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) 8859 { 8860 ImGuiContext& g = *GImGui; 8861 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); 8862 NavMoveRequestCancel(); 8863 g.NavMoveDir = move_dir; 8864 g.NavMoveClipDir = clip_dir; 8865 g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; 8866 g.NavMoveRequestFlags = move_flags; 8867 g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; 8868 } 8869 8870 void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) 8871 { 8872 ImGuiContext& g = *GImGui; 8873 8874 // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire 8875 // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. 8876 g.NavWrapRequestWindow = window; 8877 g.NavWrapRequestFlags = move_flags; 8878 } 8879 8880 // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). 8881 // This way we could find the last focused window among our children. It would be much less confusing this way? 8882 static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) 8883 { 8884 ImGuiWindow* parent = nav_window; 8885 while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 8886 parent = parent->ParentWindow; 8887 if (parent && parent != nav_window) 8888 parent->NavLastChildNavWindow = nav_window; 8889 } 8890 8891 // Restore the last focused child. 8892 // Call when we are expected to land on the Main Layer (0) after FocusWindow() 8893 static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) 8894 { 8895 if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) 8896 return window->NavLastChildNavWindow; 8897 return window; 8898 } 8899 8900 void ImGui::NavRestoreLayer(ImGuiNavLayer layer) 8901 { 8902 ImGuiContext& g = *GImGui; 8903 if (layer == ImGuiNavLayer_Main) 8904 g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); 8905 ImGuiWindow* window = g.NavWindow; 8906 if (window->NavLastIds[layer] != 0) 8907 { 8908 SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); 8909 g.NavDisableHighlight = false; 8910 g.NavDisableMouseHover = g.NavMousePosDirty = true; 8911 } 8912 else 8913 { 8914 g.NavLayer = layer; 8915 NavInitWindow(window, true); 8916 } 8917 } 8918 8919 static inline void ImGui::NavUpdateAnyRequestFlag() 8920 { 8921 ImGuiContext& g = *GImGui; 8922 g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); 8923 if (g.NavAnyRequest) 8924 IM_ASSERT(g.NavWindow != NULL); 8925 } 8926 8927 // This needs to be called before we submit any widget (aka in or before Begin) 8928 void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) 8929 { 8930 ImGuiContext& g = *GImGui; 8931 IM_ASSERT(window == g.NavWindow); 8932 8933 if (window->Flags & ImGuiWindowFlags_NoNavInputs) 8934 { 8935 g.NavId = g.NavFocusScopeId = 0; 8936 return; 8937 } 8938 8939 bool init_for_nav = false; 8940 if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) 8941 init_for_nav = true; 8942 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); 8943 if (init_for_nav) 8944 { 8945 SetNavID(0, g.NavLayer, 0, ImRect()); 8946 g.NavInitRequest = true; 8947 g.NavInitRequestFromMove = false; 8948 g.NavInitResultId = 0; 8949 g.NavInitResultRectRel = ImRect(); 8950 NavUpdateAnyRequestFlag(); 8951 } 8952 else 8953 { 8954 g.NavId = window->NavLastIds[0]; 8955 g.NavFocusScopeId = 0; 8956 } 8957 } 8958 8959 static ImVec2 ImGui::NavCalcPreferredRefPos() 8960 { 8961 ImGuiContext& g = *GImGui; 8962 if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) 8963 { 8964 // Mouse (we need a fallback in case the mouse becomes invalid after being used) 8965 if (IsMousePosValid(&g.IO.MousePos)) 8966 return g.IO.MousePos; 8967 return g.LastValidMousePos; 8968 } 8969 else 8970 { 8971 // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. 8972 const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; 8973 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())); 8974 ImGuiViewport* viewport = GetMainViewport(); 8975 return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. 8976 } 8977 } 8978 8979 float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) 8980 { 8981 ImGuiContext& g = *GImGui; 8982 if (mode == ImGuiInputReadMode_Down) 8983 return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) 8984 8985 const float t = g.IO.NavInputsDownDuration[n]; 8986 if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. 8987 return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); 8988 if (t < 0.0f) 8989 return 0.0f; 8990 if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. 8991 return (t == 0.0f) ? 1.0f : 0.0f; 8992 if (mode == ImGuiInputReadMode_Repeat) 8993 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f); 8994 if (mode == ImGuiInputReadMode_RepeatSlow) 8995 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f); 8996 if (mode == ImGuiInputReadMode_RepeatFast) 8997 return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f); 8998 return 0.0f; 8999 } 9000 9001 ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) 9002 { 9003 ImVec2 delta(0.0f, 0.0f); 9004 if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) 9005 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); 9006 if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) 9007 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); 9008 if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) 9009 delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); 9010 if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) 9011 delta *= slow_factor; 9012 if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) 9013 delta *= fast_factor; 9014 return delta; 9015 } 9016 9017 static void ImGui::NavUpdate() 9018 { 9019 ImGuiContext& g = *GImGui; 9020 ImGuiIO& io = g.IO; 9021 9022 io.WantSetMousePos = false; 9023 g.NavWrapRequestWindow = NULL; 9024 g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; 9025 #if 0 9026 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); 9027 #endif 9028 9029 // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) 9030 // (do it before we map Keyboard input!) 9031 bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; 9032 bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; 9033 if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) 9034 { 9035 if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f 9036 || io.NavInputs[ImGuiNavInput_DpadLeft] > 0.0f || io.NavInputs[ImGuiNavInput_DpadRight] > 0.0f || io.NavInputs[ImGuiNavInput_DpadUp] > 0.0f || io.NavInputs[ImGuiNavInput_DpadDown] > 0.0f) 9037 g.NavInputSource = ImGuiInputSource_Gamepad; 9038 } 9039 9040 // Update Keyboard->Nav inputs mapping 9041 if (nav_keyboard_active) 9042 { 9043 #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) 9044 NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); 9045 NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); 9046 NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); 9047 NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); 9048 NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); 9049 NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); 9050 NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); 9051 if (io.KeyCtrl) 9052 io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; 9053 if (io.KeyShift) 9054 io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; 9055 9056 // AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl) 9057 // But also even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. 9058 if (io.KeyAlt && !io.KeyCtrl) 9059 io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; 9060 9061 // We automatically cancel toggling nav layer when any text has been typed while holding Alt. (See #370) 9062 if (io.KeyAlt && !io.KeyCtrl && g.NavWindowingToggleLayer && io.InputQueueCharacters.Size > 0) 9063 g.NavWindowingToggleLayer = false; 9064 9065 #undef NAV_MAP_KEY 9066 } 9067 memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); 9068 for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) 9069 io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f; 9070 9071 // Process navigation init request (select first/default focus) 9072 if (g.NavInitResultId != 0) 9073 NavUpdateInitResult(); 9074 g.NavInitRequest = false; 9075 g.NavInitRequestFromMove = false; 9076 g.NavInitResultId = 0; 9077 g.NavJustMovedToId = 0; 9078 9079 // Process navigation move request 9080 if (g.NavMoveRequest) 9081 NavUpdateMoveResult(); 9082 9083 // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame 9084 if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) 9085 { 9086 IM_ASSERT(g.NavMoveRequest); 9087 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 9088 g.NavDisableHighlight = false; 9089 g.NavMoveRequestForward = ImGuiNavForward_None; 9090 } 9091 9092 // Apply application mouse position movement, after we had a chance to process move request result. 9093 if (g.NavMousePosDirty && g.NavIdIsAlive) 9094 { 9095 // Set mouse position given our knowledge of the navigated item position from last frame 9096 if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) 9097 if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) 9098 { 9099 io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); 9100 io.WantSetMousePos = true; 9101 } 9102 g.NavMousePosDirty = false; 9103 } 9104 g.NavIdIsAlive = false; 9105 g.NavJustTabbedId = 0; 9106 IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); 9107 9108 // 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 9109 if (g.NavWindow) 9110 NavSaveLastChildNavWindowIntoParent(g.NavWindow); 9111 if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) 9112 g.NavWindow->NavLastChildNavWindow = NULL; 9113 9114 // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) 9115 NavUpdateWindowing(); 9116 9117 // Set output flags for user application 9118 io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); 9119 io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); 9120 9121 // Process NavCancel input (to close a popup, get back to parent, clear focus) 9122 if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) 9123 { 9124 IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); 9125 if (g.ActiveId != 0) 9126 { 9127 if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) 9128 ClearActiveID(); 9129 } 9130 else if (g.NavLayer != ImGuiNavLayer_Main) 9131 { 9132 // Leave the "menu" layer 9133 NavRestoreLayer(ImGuiNavLayer_Main); 9134 } 9135 else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) 9136 { 9137 // Exit child window 9138 ImGuiWindow* child_window = g.NavWindow; 9139 ImGuiWindow* parent_window = g.NavWindow->ParentWindow; 9140 IM_ASSERT(child_window->ChildId != 0); 9141 ImRect child_rect = child_window->Rect(); 9142 FocusWindow(parent_window); 9143 SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); 9144 } 9145 else if (g.OpenPopupStack.Size > 0) 9146 { 9147 // Close open popup/menu 9148 if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) 9149 ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); 9150 } 9151 else 9152 { 9153 // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were 9154 if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) 9155 g.NavWindow->NavLastIds[0] = 0; 9156 g.NavId = g.NavFocusScopeId = 0; 9157 } 9158 } 9159 9160 // Process manual activation request 9161 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; 9162 if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 9163 { 9164 bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); 9165 bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); 9166 if (g.ActiveId == 0 && activate_pressed) 9167 g.NavActivateId = g.NavId; 9168 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) 9169 g.NavActivateDownId = g.NavId; 9170 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) 9171 g.NavActivatePressedId = g.NavId; 9172 if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) 9173 g.NavInputId = g.NavId; 9174 } 9175 if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 9176 g.NavDisableHighlight = true; 9177 if (g.NavActivateId != 0) 9178 IM_ASSERT(g.NavActivateDownId == g.NavActivateId); 9179 g.NavMoveRequest = false; 9180 9181 // Process programmatic activation request 9182 if (g.NavNextActivateId != 0) 9183 g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; 9184 g.NavNextActivateId = 0; 9185 9186 // Initiate directional inputs request 9187 if (g.NavMoveRequestForward == ImGuiNavForward_None) 9188 { 9189 g.NavMoveDir = ImGuiDir_None; 9190 g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; 9191 if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) 9192 { 9193 const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; 9194 if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } 9195 if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } 9196 if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } 9197 if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } 9198 } 9199 g.NavMoveClipDir = g.NavMoveDir; 9200 } 9201 else 9202 { 9203 // 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) 9204 // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) 9205 IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); 9206 IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); 9207 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); 9208 g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; 9209 } 9210 9211 // Update PageUp/PageDown/Home/End scroll 9212 // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? 9213 float nav_scoring_rect_offset_y = 0.0f; 9214 if (nav_keyboard_active) 9215 nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); 9216 9217 // 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 9218 if (g.NavMoveDir != ImGuiDir_None) 9219 { 9220 g.NavMoveRequest = true; 9221 g.NavMoveRequestKeyMods = io.KeyMods; 9222 g.NavMoveDirLast = g.NavMoveDir; 9223 } 9224 if (g.NavMoveRequest && g.NavId == 0) 9225 { 9226 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); 9227 g.NavInitRequest = g.NavInitRequestFromMove = true; 9228 // Reassigning with same value, we're being explicit here. 9229 g.NavInitResultId = 0; // -V1048 9230 g.NavDisableHighlight = false; 9231 } 9232 NavUpdateAnyRequestFlag(); 9233 9234 // Scrolling 9235 if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) 9236 { 9237 // *Fallback* manual-scroll with Nav directional keys when window has no navigable item 9238 ImGuiWindow* window = g.NavWindow; 9239 const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. 9240 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) 9241 { 9242 if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) 9243 SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); 9244 if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) 9245 SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); 9246 } 9247 9248 // *Normal* Manual scroll with NavScrollXXX keys 9249 // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. 9250 ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f / 10.0f, 10.0f); 9251 if (scroll_dir.x != 0.0f && window->ScrollbarX) 9252 SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); 9253 if (scroll_dir.y != 0.0f) 9254 SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); 9255 } 9256 9257 // Reset search results 9258 g.NavMoveResultLocal.Clear(); 9259 g.NavMoveResultLocalVisibleSet.Clear(); 9260 g.NavMoveResultOther.Clear(); 9261 9262 // When using gamepad, we project the reference nav bounding box into window visible area. 9263 // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative 9264 // (can't focus a visible object like we can with the mouse). 9265 if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main) 9266 { 9267 ImGuiWindow* window = g.NavWindow; 9268 ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); 9269 if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) 9270 { 9271 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); 9272 float pad = window->CalcFontSize() * 0.5f; 9273 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 9274 window->NavRectRel[g.NavLayer].ClipWithFull(window_rect_rel); 9275 g.NavId = g.NavFocusScopeId = 0; 9276 } 9277 } 9278 9279 // 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) 9280 ImRect nav_rect_rel = g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted() ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); 9281 g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0); 9282 g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); 9283 g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); 9284 g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; 9285 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(). 9286 //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] 9287 g.NavScoringCount = 0; 9288 #if IMGUI_DEBUG_NAV_RECTS 9289 if (g.NavWindow) 9290 { 9291 ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); 9292 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] 9293 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); } 9294 } 9295 #endif 9296 } 9297 9298 static void ImGui::NavUpdateInitResult() 9299 { 9300 // 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) 9301 ImGuiContext& g = *GImGui; 9302 if (!g.NavWindow) 9303 return; 9304 9305 // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) 9306 // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. 9307 IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); 9308 SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); 9309 if (g.NavInitRequestFromMove) 9310 { 9311 g.NavDisableHighlight = false; 9312 g.NavDisableMouseHover = g.NavMousePosDirty = true; 9313 } 9314 } 9315 9316 // Apply result from previous frame navigation directional move request 9317 static void ImGui::NavUpdateMoveResult() 9318 { 9319 ImGuiContext& g = *GImGui; 9320 if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) 9321 { 9322 // 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) 9323 if (g.NavId != 0) 9324 { 9325 g.NavDisableHighlight = false; 9326 g.NavDisableMouseHover = true; 9327 } 9328 return; 9329 } 9330 9331 // Select which result to use 9332 ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; 9333 9334 // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. 9335 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) 9336 if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) 9337 result = &g.NavMoveResultLocalVisibleSet; 9338 9339 // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. 9340 if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) 9341 if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) 9342 result = &g.NavMoveResultOther; 9343 IM_ASSERT(g.NavWindow && result->Window); 9344 9345 // Scroll to keep newly navigated item fully into view. 9346 if (g.NavLayer == ImGuiNavLayer_Main) 9347 { 9348 ImVec2 delta_scroll; 9349 if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) 9350 { 9351 float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; 9352 delta_scroll.y = result->Window->Scroll.y - scroll_target; 9353 SetScrollY(result->Window, scroll_target); 9354 } 9355 else 9356 { 9357 ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); 9358 delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); 9359 } 9360 9361 // Offset our result position so mouse position can be applied immediately after in NavUpdate() 9362 result->RectRel.TranslateX(-delta_scroll.x); 9363 result->RectRel.TranslateY(-delta_scroll.y); 9364 } 9365 9366 ClearActiveID(); 9367 g.NavWindow = result->Window; 9368 if (g.NavId != result->ID) 9369 { 9370 // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) 9371 g.NavJustMovedToId = result->ID; 9372 g.NavJustMovedToFocusScopeId = result->FocusScopeId; 9373 g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; 9374 } 9375 IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); 9376 SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); 9377 g.NavDisableHighlight = false; 9378 g.NavDisableMouseHover = g.NavMousePosDirty = true; 9379 } 9380 9381 // Handle PageUp/PageDown/Home/End keys 9382 static float ImGui::NavUpdatePageUpPageDown() 9383 { 9384 ImGuiContext& g = *GImGui; 9385 ImGuiIO& io = g.IO; 9386 9387 if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) 9388 return 0.0f; 9389 if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) 9390 return 0.0f; 9391 9392 ImGuiWindow* window = g.NavWindow; 9393 const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); 9394 const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); 9395 const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); 9396 const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); 9397 if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed 9398 { 9399 if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) 9400 { 9401 // Fallback manual-scroll when window has no navigable item 9402 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) 9403 SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); 9404 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) 9405 SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); 9406 else if (home_pressed) 9407 SetScrollY(window, 0.0f); 9408 else if (end_pressed) 9409 SetScrollY(window, window->ScrollMax.y); 9410 } 9411 else 9412 { 9413 ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; 9414 const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); 9415 float nav_scoring_rect_offset_y = 0.0f; 9416 if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) 9417 { 9418 nav_scoring_rect_offset_y = -page_offset_y; 9419 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) 9420 g.NavMoveClipDir = ImGuiDir_Up; 9421 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; 9422 } 9423 else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) 9424 { 9425 nav_scoring_rect_offset_y = +page_offset_y; 9426 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) 9427 g.NavMoveClipDir = ImGuiDir_Down; 9428 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; 9429 } 9430 else if (home_pressed) 9431 { 9432 // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y 9433 // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. 9434 // Preserve current horizontal position if we have any. 9435 nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; 9436 if (nav_rect_rel.IsInverted()) 9437 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; 9438 g.NavMoveDir = ImGuiDir_Down; 9439 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; 9440 } 9441 else if (end_pressed) 9442 { 9443 nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; 9444 if (nav_rect_rel.IsInverted()) 9445 nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; 9446 g.NavMoveDir = ImGuiDir_Up; 9447 g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; 9448 } 9449 return nav_scoring_rect_offset_y; 9450 } 9451 } 9452 return 0.0f; 9453 } 9454 9455 static void ImGui::NavEndFrame() 9456 { 9457 ImGuiContext& g = *GImGui; 9458 9459 // Show CTRL+TAB list window 9460 if (g.NavWindowingTarget != NULL) 9461 NavUpdateWindowingOverlay(); 9462 9463 // Perform wrap-around in menus 9464 ImGuiWindow* window = g.NavWrapRequestWindow; 9465 ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags; 9466 if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main) 9467 { 9468 IM_ASSERT(move_flags != 0); // No points calling this with no wrapping 9469 ImRect bb_rel = window->NavRectRel[0]; 9470 9471 ImGuiDir clip_dir = g.NavMoveDir; 9472 if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) 9473 { 9474 bb_rel.Min.x = bb_rel.Max.x = 9475 ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; 9476 if (move_flags & ImGuiNavMoveFlags_WrapX) 9477 { 9478 bb_rel.TranslateY(-bb_rel.GetHeight()); 9479 clip_dir = ImGuiDir_Up; 9480 } 9481 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9482 } 9483 if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) 9484 { 9485 bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; 9486 if (move_flags & ImGuiNavMoveFlags_WrapX) 9487 { 9488 bb_rel.TranslateY(+bb_rel.GetHeight()); 9489 clip_dir = ImGuiDir_Down; 9490 } 9491 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9492 } 9493 if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) 9494 { 9495 bb_rel.Min.y = bb_rel.Max.y = 9496 ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; 9497 if (move_flags & ImGuiNavMoveFlags_WrapY) 9498 { 9499 bb_rel.TranslateX(-bb_rel.GetWidth()); 9500 clip_dir = ImGuiDir_Left; 9501 } 9502 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9503 } 9504 if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) 9505 { 9506 bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; 9507 if (move_flags & ImGuiNavMoveFlags_WrapY) 9508 { 9509 bb_rel.TranslateX(+bb_rel.GetWidth()); 9510 clip_dir = ImGuiDir_Right; 9511 } 9512 NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); 9513 } 9514 } 9515 } 9516 9517 static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) 9518 { 9519 ImGuiContext& g = *GImGui; 9520 IM_UNUSED(g); 9521 int order = window->FocusOrder; 9522 IM_ASSERT(g.WindowsFocusOrder[order] == window); 9523 return order; 9524 } 9525 9526 static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) 9527 { 9528 ImGuiContext& g = *GImGui; 9529 for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) 9530 if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) 9531 return g.WindowsFocusOrder[i]; 9532 return NULL; 9533 } 9534 9535 static void NavUpdateWindowingHighlightWindow(int focus_change_dir) 9536 { 9537 ImGuiContext& g = *GImGui; 9538 IM_ASSERT(g.NavWindowingTarget); 9539 if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) 9540 return; 9541 9542 const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget); 9543 ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); 9544 if (!window_target) 9545 window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); 9546 if (window_target) // Don't reset windowing target if there's a single window in the list 9547 g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; 9548 g.NavWindowingToggleLayer = false; 9549 } 9550 9551 // Windowing management mode 9552 // Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) 9553 // Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) 9554 static void ImGui::NavUpdateWindowing() 9555 { 9556 ImGuiContext& g = *GImGui; 9557 ImGuiWindow* apply_focus_window = NULL; 9558 bool apply_toggle_layer = false; 9559 9560 ImGuiWindow* modal_window = GetTopMostPopupModal(); 9561 bool allow_windowing = (modal_window == NULL); 9562 if (!allow_windowing) 9563 g.NavWindowingTarget = NULL; 9564 9565 // Fade out 9566 if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) 9567 { 9568 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); 9569 if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) 9570 g.NavWindowingTargetAnim = NULL; 9571 } 9572 9573 // Start CTRL-TAB or Square+L/R window selection 9574 bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); 9575 bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); 9576 if (start_windowing_with_gamepad || start_windowing_with_keyboard) 9577 if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) 9578 { 9579 g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop 9580 g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; 9581 g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; 9582 g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; 9583 } 9584 9585 // Gamepad update 9586 g.NavWindowingTimer += g.IO.DeltaTime; 9587 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad) 9588 { 9589 // 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 9590 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); 9591 9592 // Select window to focus 9593 const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); 9594 if (focus_change_dir != 0) 9595 { 9596 NavUpdateWindowingHighlightWindow(focus_change_dir); 9597 g.NavWindowingHighlightAlpha = 1.0f; 9598 } 9599 9600 // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) 9601 if (!IsNavInputDown(ImGuiNavInput_Menu)) 9602 { 9603 g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. 9604 if (g.NavWindowingToggleLayer && g.NavWindow) 9605 apply_toggle_layer = true; 9606 else if (!g.NavWindowingToggleLayer) 9607 apply_focus_window = g.NavWindowingTarget; 9608 g.NavWindowingTarget = NULL; 9609 } 9610 } 9611 9612 // Keyboard: Focus 9613 if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard) 9614 { 9615 // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise 9616 g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f 9617 if (IsKeyPressedMap(ImGuiKey_Tab, true)) 9618 NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); 9619 if (!g.IO.KeyCtrl) 9620 apply_focus_window = g.NavWindowingTarget; 9621 } 9622 9623 // Keyboard: Press and Release ALT to toggle menu layer 9624 // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB 9625 if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) 9626 g.NavWindowingToggleLayer = true; 9627 if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) 9628 if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) 9629 apply_toggle_layer = true; 9630 9631 // Move window 9632 if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) 9633 { 9634 ImVec2 move_delta; 9635 if (g.NavInputSource == ImGuiInputSource_Keyboard && !g.IO.KeyShift) 9636 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); 9637 if (g.NavInputSource == ImGuiInputSource_Gamepad) 9638 move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); 9639 if (move_delta.x != 0.0f || move_delta.y != 0.0f) 9640 { 9641 const float NAV_MOVE_SPEED = 800.0f; 9642 const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well 9643 ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; 9644 SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); 9645 MarkIniSettingsDirty(moving_window); 9646 g.NavDisableMouseHover = true; 9647 } 9648 } 9649 9650 // Apply final focus 9651 if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) 9652 { 9653 ClearActiveID(); 9654 g.NavDisableHighlight = false; 9655 g.NavDisableMouseHover = true; 9656 apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); 9657 ClosePopupsOverWindow(apply_focus_window, false); 9658 FocusWindow(apply_focus_window); 9659 if (apply_focus_window->NavLastIds[0] == 0) 9660 NavInitWindow(apply_focus_window, false); 9661 9662 // If the window has ONLY a menu layer (no main layer), select it directly 9663 // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, 9664 // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since 9665 // the target window as already been previewed once. 9666 // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, 9667 // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* 9668 // won't be valid. 9669 if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) 9670 g.NavLayer = ImGuiNavLayer_Menu; 9671 } 9672 if (apply_focus_window) 9673 g.NavWindowingTarget = NULL; 9674 9675 // Apply menu/layer toggle 9676 if (apply_toggle_layer && g.NavWindow) 9677 { 9678 ClearActiveID(); 9679 9680 // Move to parent menu if necessary 9681 ImGuiWindow* new_nav_window = g.NavWindow; 9682 while (new_nav_window->ParentWindow 9683 && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 9684 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 9685 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) 9686 new_nav_window = new_nav_window->ParentWindow; 9687 if (new_nav_window != g.NavWindow) 9688 { 9689 ImGuiWindow* old_nav_window = g.NavWindow; 9690 FocusWindow(new_nav_window); 9691 new_nav_window->NavLastChildNavWindow = old_nav_window; 9692 } 9693 g.NavDisableHighlight = false; 9694 g.NavDisableMouseHover = true; 9695 9696 // Reinitialize navigation when entering menu bar with the Alt key. 9697 const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; 9698 if (new_nav_layer == ImGuiNavLayer_Menu) 9699 g.NavWindow->NavLastIds[new_nav_layer] = 0; 9700 NavRestoreLayer(new_nav_layer); 9701 } 9702 } 9703 9704 // Window has already passed the IsWindowNavFocusable() 9705 static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) 9706 { 9707 if (window->Flags & ImGuiWindowFlags_Popup) 9708 return "(Popup)"; 9709 if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) 9710 return "(Main menu bar)"; 9711 return "(Untitled)"; 9712 } 9713 9714 // Overlay displayed when using CTRL+TAB. Called by EndFrame(). 9715 void ImGui::NavUpdateWindowingOverlay() 9716 { 9717 ImGuiContext& g = *GImGui; 9718 IM_ASSERT(g.NavWindowingTarget != NULL); 9719 9720 if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) 9721 return; 9722 9723 if (g.NavWindowingListWindow == NULL) 9724 g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); 9725 const ImGuiViewport* viewport = GetMainViewport(); 9726 SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); 9727 SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 9728 PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); 9729 Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); 9730 for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) 9731 { 9732 ImGuiWindow* window = g.WindowsFocusOrder[n]; 9733 IM_ASSERT(window != NULL); // Fix static analyzers 9734 if (!IsWindowNavFocusable(window)) 9735 continue; 9736 const char* label = window->Name; 9737 if (label == FindRenderedTextEnd(label)) 9738 label = GetFallbackWindowNameForWindowingList(window); 9739 Selectable(label, g.NavWindowingTarget == window); 9740 } 9741 End(); 9742 PopStyleVar(); 9743 } 9744 9745 9746 //----------------------------------------------------------------------------- 9747 // [SECTION] DRAG AND DROP 9748 //----------------------------------------------------------------------------- 9749 9750 void ImGui::ClearDragDrop() 9751 { 9752 ImGuiContext& g = *GImGui; 9753 g.DragDropActive = false; 9754 g.DragDropPayload.Clear(); 9755 g.DragDropAcceptFlags = ImGuiDragDropFlags_None; 9756 g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; 9757 g.DragDropAcceptIdCurrRectSurface = FLT_MAX; 9758 g.DragDropAcceptFrameCount = -1; 9759 9760 g.DragDropPayloadBufHeap.clear(); 9761 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 9762 } 9763 9764 // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() 9765 // If the item has an identifier: 9766 // - This assume/require the item to be activated (typically via ButtonBehavior). 9767 // - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button. 9768 // - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag. 9769 // If the item has no identifier: 9770 // - Currently always assume left mouse button. 9771 bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) 9772 { 9773 ImGuiContext& g = *GImGui; 9774 ImGuiWindow* window = g.CurrentWindow; 9775 9776 // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button, 9777 // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic). 9778 ImGuiMouseButton mouse_button = ImGuiMouseButton_Left; 9779 9780 bool source_drag_active = false; 9781 ImGuiID source_id = 0; 9782 ImGuiID source_parent_id = 0; 9783 if (!(flags & ImGuiDragDropFlags_SourceExtern)) 9784 { 9785 source_id = window->DC.LastItemId; 9786 if (source_id != 0) 9787 { 9788 // Common path: items with ID 9789 if (g.ActiveId != source_id) 9790 return false; 9791 if (g.ActiveIdMouseButton != -1) 9792 mouse_button = g.ActiveIdMouseButton; 9793 if (g.IO.MouseDown[mouse_button] == false) 9794 return false; 9795 g.ActiveIdAllowOverlap = false; 9796 } 9797 else 9798 { 9799 // Uncommon path: items without ID 9800 if (g.IO.MouseDown[mouse_button] == false) 9801 return false; 9802 9803 // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: 9804 // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. 9805 if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) 9806 { 9807 IM_ASSERT(0); 9808 return false; 9809 } 9810 9811 // Early out 9812 if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) 9813 return false; 9814 9815 // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() 9816 // We build a throwaway ID based on current ID stack + relative AABB of items in window. 9817 // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. 9818 // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. 9819 source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); 9820 bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); 9821 if (is_hovered && g.IO.MouseClicked[mouse_button]) 9822 { 9823 SetActiveID(source_id, window); 9824 FocusWindow(window); 9825 } 9826 if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. 9827 g.ActiveIdAllowOverlap = is_hovered; 9828 } 9829 if (g.ActiveId != source_id) 9830 return false; 9831 source_parent_id = window->IDStack.back(); 9832 source_drag_active = IsMouseDragging(mouse_button); 9833 9834 // Disable navigation and key inputs while dragging 9835 g.ActiveIdUsingNavDirMask = ~(ImU32)0; 9836 g.ActiveIdUsingNavInputMask = ~(ImU32)0; 9837 g.ActiveIdUsingKeyInputMask = ~(ImU64)0; 9838 } 9839 else 9840 { 9841 window = NULL; 9842 source_id = ImHashStr("#SourceExtern"); 9843 source_drag_active = true; 9844 } 9845 9846 if (source_drag_active) 9847 { 9848 if (!g.DragDropActive) 9849 { 9850 IM_ASSERT(source_id != 0); 9851 ClearDragDrop(); 9852 ImGuiPayload& payload = g.DragDropPayload; 9853 payload.SourceId = source_id; 9854 payload.SourceParentId = source_parent_id; 9855 g.DragDropActive = true; 9856 g.DragDropSourceFlags = flags; 9857 g.DragDropMouseButton = mouse_button; 9858 if (payload.SourceId == g.ActiveId) 9859 g.ActiveIdNoClearOnFocusLoss = true; 9860 } 9861 g.DragDropSourceFrameCount = g.FrameCount; 9862 g.DragDropWithinSource = true; 9863 9864 if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 9865 { 9866 // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) 9867 // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. 9868 BeginTooltip(); 9869 if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) 9870 { 9871 ImGuiWindow* tooltip_window = g.CurrentWindow; 9872 tooltip_window->SkipItems = true; 9873 tooltip_window->HiddenFramesCanSkipItems = 1; 9874 } 9875 } 9876 9877 if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) 9878 window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; 9879 9880 return true; 9881 } 9882 return false; 9883 } 9884 9885 void ImGui::EndDragDropSource() 9886 { 9887 ImGuiContext& g = *GImGui; 9888 IM_ASSERT(g.DragDropActive); 9889 IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?"); 9890 9891 if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) 9892 EndTooltip(); 9893 9894 // Discard the drag if have not called SetDragDropPayload() 9895 if (g.DragDropPayload.DataFrameCount == -1) 9896 ClearDragDrop(); 9897 g.DragDropWithinSource = false; 9898 } 9899 9900 // Use 'cond' to choose to submit payload on drag start or every frame 9901 bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) 9902 { 9903 ImGuiContext& g = *GImGui; 9904 ImGuiPayload& payload = g.DragDropPayload; 9905 if (cond == 0) 9906 cond = ImGuiCond_Always; 9907 9908 IM_ASSERT(type != NULL); 9909 IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); 9910 IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); 9911 IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); 9912 IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() 9913 9914 if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) 9915 { 9916 // Copy payload 9917 ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); 9918 g.DragDropPayloadBufHeap.resize(0); 9919 if (data_size > sizeof(g.DragDropPayloadBufLocal)) 9920 { 9921 // Store in heap 9922 g.DragDropPayloadBufHeap.resize((int)data_size); 9923 payload.Data = g.DragDropPayloadBufHeap.Data; 9924 memcpy(payload.Data, data, data_size); 9925 } 9926 else if (data_size > 0) 9927 { 9928 // Store locally 9929 memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); 9930 payload.Data = g.DragDropPayloadBufLocal; 9931 memcpy(payload.Data, data, data_size); 9932 } 9933 else 9934 { 9935 payload.Data = NULL; 9936 } 9937 payload.DataSize = (int)data_size; 9938 } 9939 payload.DataFrameCount = g.FrameCount; 9940 9941 return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); 9942 } 9943 9944 bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) 9945 { 9946 ImGuiContext& g = *GImGui; 9947 if (!g.DragDropActive) 9948 return false; 9949 9950 ImGuiWindow* window = g.CurrentWindow; 9951 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; 9952 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) 9953 return false; 9954 IM_ASSERT(id != 0); 9955 if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) 9956 return false; 9957 if (window->SkipItems) 9958 return false; 9959 9960 IM_ASSERT(g.DragDropWithinTarget == false); 9961 g.DragDropTargetRect = bb; 9962 g.DragDropTargetId = id; 9963 g.DragDropWithinTarget = true; 9964 return true; 9965 } 9966 9967 // We don't use BeginDragDropTargetCustom() and duplicate its code because: 9968 // 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. 9969 // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. 9970 // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) 9971 bool ImGui::BeginDragDropTarget() 9972 { 9973 ImGuiContext& g = *GImGui; 9974 if (!g.DragDropActive) 9975 return false; 9976 9977 ImGuiWindow* window = g.CurrentWindow; 9978 if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) 9979 return false; 9980 ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; 9981 if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) 9982 return false; 9983 9984 const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; 9985 ImGuiID id = window->DC.LastItemId; 9986 if (id == 0) 9987 id = window->GetIDFromRectangle(display_rect); 9988 if (g.DragDropPayload.SourceId == id) 9989 return false; 9990 9991 IM_ASSERT(g.DragDropWithinTarget == false); 9992 g.DragDropTargetRect = display_rect; 9993 g.DragDropTargetId = id; 9994 g.DragDropWithinTarget = true; 9995 return true; 9996 } 9997 9998 bool ImGui::IsDragDropPayloadBeingAccepted() 9999 { 10000 ImGuiContext& g = *GImGui; 10001 return g.DragDropActive && g.DragDropAcceptIdPrev != 0; 10002 } 10003 10004 const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) 10005 { 10006 ImGuiContext& g = *GImGui; 10007 ImGuiWindow* window = g.CurrentWindow; 10008 ImGuiPayload& payload = g.DragDropPayload; 10009 IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? 10010 IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? 10011 if (type != NULL && !payload.IsDataType(type)) 10012 return NULL; 10013 10014 // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. 10015 // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! 10016 const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); 10017 ImRect r = g.DragDropTargetRect; 10018 float r_surface = r.GetWidth() * r.GetHeight(); 10019 if (r_surface <= g.DragDropAcceptIdCurrRectSurface) 10020 { 10021 g.DragDropAcceptFlags = flags; 10022 g.DragDropAcceptIdCurr = g.DragDropTargetId; 10023 g.DragDropAcceptIdCurrRectSurface = r_surface; 10024 } 10025 10026 // Render default drop visuals 10027 payload.Preview = was_accepted_previously; 10028 flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) 10029 if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) 10030 { 10031 // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. 10032 r.Expand(3.5f); 10033 bool push_clip_rect = !window->ClipRect.Contains(r); 10034 if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1)); 10035 window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); 10036 if (push_clip_rect) window->DrawList->PopClipRect(); 10037 } 10038 10039 g.DragDropAcceptFrameCount = g.FrameCount; 10040 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() 10041 if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) 10042 return NULL; 10043 10044 return &payload; 10045 } 10046 10047 const ImGuiPayload* ImGui::GetDragDropPayload() 10048 { 10049 ImGuiContext& g = *GImGui; 10050 return g.DragDropActive ? &g.DragDropPayload : NULL; 10051 } 10052 10053 // We don't really use/need this now, but added it for the sake of consistency and because we might need it later. 10054 void ImGui::EndDragDropTarget() 10055 { 10056 ImGuiContext& g = *GImGui; 10057 IM_ASSERT(g.DragDropActive); 10058 IM_ASSERT(g.DragDropWithinTarget); 10059 g.DragDropWithinTarget = false; 10060 } 10061 10062 //----------------------------------------------------------------------------- 10063 // [SECTION] LOGGING/CAPTURING 10064 //----------------------------------------------------------------------------- 10065 // All text output from the interface can be captured into tty/file/clipboard. 10066 // By default, tree nodes are automatically opened during logging. 10067 //----------------------------------------------------------------------------- 10068 10069 // Pass text data straight to log (without being displayed) 10070 static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args) 10071 { 10072 if (g.LogFile) 10073 { 10074 g.LogBuffer.Buf.resize(0); 10075 g.LogBuffer.appendfv(fmt, args); 10076 ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile); 10077 } 10078 else 10079 { 10080 g.LogBuffer.appendfv(fmt, args); 10081 } 10082 } 10083 10084 void ImGui::LogText(const char* fmt, ...) 10085 { 10086 ImGuiContext& g = *GImGui; 10087 if (!g.LogEnabled) 10088 return; 10089 10090 va_list args; 10091 va_start(args, fmt); 10092 LogTextV(g, fmt, args); 10093 va_end(args); 10094 } 10095 10096 void ImGui::LogTextV(const char* fmt, va_list args) 10097 { 10098 ImGuiContext& g = *GImGui; 10099 if (!g.LogEnabled) 10100 return; 10101 10102 LogTextV(g, fmt, args); 10103 } 10104 10105 // Internal version that takes a position to decide on newline placement and pad items according to their depth. 10106 // We split text into individual lines to add current tree level padding 10107 // FIXME: This code is a little complicated perhaps, considering simplifying the whole system. 10108 void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) 10109 { 10110 ImGuiContext& g = *GImGui; 10111 ImGuiWindow* window = g.CurrentWindow; 10112 10113 const char* prefix = g.LogNextPrefix; 10114 const char* suffix = g.LogNextSuffix; 10115 g.LogNextPrefix = g.LogNextSuffix = NULL; 10116 10117 if (!text_end) 10118 text_end = FindRenderedTextEnd(text, text_end); 10119 10120 const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1); 10121 if (ref_pos) 10122 g.LogLinePosY = ref_pos->y; 10123 if (log_new_line) 10124 { 10125 LogText(IM_NEWLINE); 10126 g.LogLineFirstItem = true; 10127 } 10128 10129 if (prefix) 10130 LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure "##" are included here. 10131 10132 // Re-adjust padding if we have popped out of our starting depth 10133 if (g.LogDepthRef > window->DC.TreeDepth) 10134 g.LogDepthRef = window->DC.TreeDepth; 10135 const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); 10136 10137 const char* text_remaining = text; 10138 for (;;) 10139 { 10140 // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry. 10141 // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured. 10142 const char* line_start = text_remaining; 10143 const char* line_end = ImStreolRange(line_start, text_end); 10144 const bool is_last_line = (line_end == text_end); 10145 if (line_start != line_end || !is_last_line) 10146 { 10147 const int line_length = (int)(line_end - line_start); 10148 const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1; 10149 LogText("%*s%.*s", indentation, "", line_length, line_start); 10150 g.LogLineFirstItem = false; 10151 if (*line_end == '\n') 10152 { 10153 LogText(IM_NEWLINE); 10154 g.LogLineFirstItem = true; 10155 } 10156 } 10157 if (is_last_line) 10158 break; 10159 text_remaining = line_end + 1; 10160 } 10161 10162 if (suffix) 10163 LogRenderedText(ref_pos, suffix, suffix + strlen(suffix)); 10164 } 10165 10166 // Start logging/capturing text output 10167 void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) 10168 { 10169 ImGuiContext& g = *GImGui; 10170 ImGuiWindow* window = g.CurrentWindow; 10171 IM_ASSERT(g.LogEnabled == false); 10172 IM_ASSERT(g.LogFile == NULL); 10173 IM_ASSERT(g.LogBuffer.empty()); 10174 g.LogEnabled = true; 10175 g.LogType = type; 10176 g.LogNextPrefix = g.LogNextSuffix = NULL; 10177 g.LogDepthRef = window->DC.TreeDepth; 10178 g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); 10179 g.LogLinePosY = FLT_MAX; 10180 g.LogLineFirstItem = true; 10181 } 10182 10183 // Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText) 10184 void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix) 10185 { 10186 ImGuiContext& g = *GImGui; 10187 g.LogNextPrefix = prefix; 10188 g.LogNextSuffix = suffix; 10189 } 10190 10191 void ImGui::LogToTTY(int auto_open_depth) 10192 { 10193 ImGuiContext& g = *GImGui; 10194 if (g.LogEnabled) 10195 return; 10196 IM_UNUSED(auto_open_depth); 10197 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 10198 LogBegin(ImGuiLogType_TTY, auto_open_depth); 10199 g.LogFile = stdout; 10200 #endif 10201 } 10202 10203 // Start logging/capturing text output to given file 10204 void ImGui::LogToFile(int auto_open_depth, const char* filename) 10205 { 10206 ImGuiContext& g = *GImGui; 10207 if (g.LogEnabled) 10208 return; 10209 10210 // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still 10211 // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. 10212 // By opening the file in binary mode "ab" we have consistent output everywhere. 10213 if (!filename) 10214 filename = g.IO.LogFilename; 10215 if (!filename || !filename[0]) 10216 return; 10217 ImFileHandle f = ImFileOpen(filename, "ab"); 10218 if (!f) 10219 { 10220 IM_ASSERT(0); 10221 return; 10222 } 10223 10224 LogBegin(ImGuiLogType_File, auto_open_depth); 10225 g.LogFile = f; 10226 } 10227 10228 // Start logging/capturing text output to clipboard 10229 void ImGui::LogToClipboard(int auto_open_depth) 10230 { 10231 ImGuiContext& g = *GImGui; 10232 if (g.LogEnabled) 10233 return; 10234 LogBegin(ImGuiLogType_Clipboard, auto_open_depth); 10235 } 10236 10237 void ImGui::LogToBuffer(int auto_open_depth) 10238 { 10239 ImGuiContext& g = *GImGui; 10240 if (g.LogEnabled) 10241 return; 10242 LogBegin(ImGuiLogType_Buffer, auto_open_depth); 10243 } 10244 10245 void ImGui::LogFinish() 10246 { 10247 ImGuiContext& g = *GImGui; 10248 if (!g.LogEnabled) 10249 return; 10250 10251 LogText(IM_NEWLINE); 10252 switch (g.LogType) 10253 { 10254 case ImGuiLogType_TTY: 10255 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 10256 fflush(g.LogFile); 10257 #endif 10258 break; 10259 case ImGuiLogType_File: 10260 ImFileClose(g.LogFile); 10261 break; 10262 case ImGuiLogType_Buffer: 10263 break; 10264 case ImGuiLogType_Clipboard: 10265 if (!g.LogBuffer.empty()) 10266 SetClipboardText(g.LogBuffer.begin()); 10267 break; 10268 case ImGuiLogType_None: 10269 IM_ASSERT(0); 10270 break; 10271 } 10272 10273 g.LogEnabled = false; 10274 g.LogType = ImGuiLogType_None; 10275 g.LogFile = NULL; 10276 g.LogBuffer.clear(); 10277 } 10278 10279 // Helper to display logging buttons 10280 // FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) 10281 void ImGui::LogButtons() 10282 { 10283 ImGuiContext& g = *GImGui; 10284 10285 PushID("LogButtons"); 10286 #ifndef IMGUI_DISABLE_TTY_FUNCTIONS 10287 const bool log_to_tty = Button("Log To TTY"); SameLine(); 10288 #else 10289 const bool log_to_tty = false; 10290 #endif 10291 const bool log_to_file = Button("Log To File"); SameLine(); 10292 const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); 10293 PushAllowKeyboardFocus(false); 10294 SetNextItemWidth(80.0f); 10295 SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); 10296 PopAllowKeyboardFocus(); 10297 PopID(); 10298 10299 // Start logging at the end of the function so that the buttons don't appear in the log 10300 if (log_to_tty) 10301 LogToTTY(); 10302 if (log_to_file) 10303 LogToFile(); 10304 if (log_to_clipboard) 10305 LogToClipboard(); 10306 } 10307 10308 10309 //----------------------------------------------------------------------------- 10310 // [SECTION] SETTINGS 10311 //----------------------------------------------------------------------------- 10312 // - UpdateSettings() [Internal] 10313 // - MarkIniSettingsDirty() [Internal] 10314 // - CreateNewWindowSettings() [Internal] 10315 // - FindWindowSettings() [Internal] 10316 // - FindOrCreateWindowSettings() [Internal] 10317 // - FindSettingsHandler() [Internal] 10318 // - ClearIniSettings() [Internal] 10319 // - LoadIniSettingsFromDisk() 10320 // - LoadIniSettingsFromMemory() 10321 // - SaveIniSettingsToDisk() 10322 // - SaveIniSettingsToMemory() 10323 // - WindowSettingsHandler_***() [Internal] 10324 //----------------------------------------------------------------------------- 10325 10326 // Called by NewFrame() 10327 void ImGui::UpdateSettings() 10328 { 10329 // Load settings on first frame (if not explicitly loaded manually before) 10330 ImGuiContext& g = *GImGui; 10331 if (!g.SettingsLoaded) 10332 { 10333 IM_ASSERT(g.SettingsWindows.empty()); 10334 if (g.IO.IniFilename) 10335 LoadIniSettingsFromDisk(g.IO.IniFilename); 10336 g.SettingsLoaded = true; 10337 } 10338 10339 // Save settings (with a delay after the last modification, so we don't spam disk too much) 10340 if (g.SettingsDirtyTimer > 0.0f) 10341 { 10342 g.SettingsDirtyTimer -= g.IO.DeltaTime; 10343 if (g.SettingsDirtyTimer <= 0.0f) 10344 { 10345 if (g.IO.IniFilename != NULL) 10346 SaveIniSettingsToDisk(g.IO.IniFilename); 10347 else 10348 g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. 10349 g.SettingsDirtyTimer = 0.0f; 10350 } 10351 } 10352 } 10353 10354 void ImGui::MarkIniSettingsDirty() 10355 { 10356 ImGuiContext& g = *GImGui; 10357 if (g.SettingsDirtyTimer <= 0.0f) 10358 g.SettingsDirtyTimer = g.IO.IniSavingRate; 10359 } 10360 10361 void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) 10362 { 10363 ImGuiContext& g = *GImGui; 10364 if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) 10365 if (g.SettingsDirtyTimer <= 0.0f) 10366 g.SettingsDirtyTimer = g.IO.IniSavingRate; 10367 } 10368 10369 ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) 10370 { 10371 ImGuiContext& g = *GImGui; 10372 10373 #if !IMGUI_DEBUG_INI_SETTINGS 10374 // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() 10375 // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. 10376 if (const char* p = strstr(name, "###")) 10377 name = p; 10378 #endif 10379 const size_t name_len = strlen(name); 10380 10381 // Allocate chunk 10382 const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1; 10383 ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size); 10384 IM_PLACEMENT_NEW(settings) ImGuiWindowSettings(); 10385 settings->ID = ImHashStr(name, name_len); 10386 memcpy(settings->GetName(), name, name_len + 1); // Store with zero terminator 10387 10388 return settings; 10389 } 10390 10391 ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) 10392 { 10393 ImGuiContext& g = *GImGui; 10394 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 10395 if (settings->ID == id) 10396 return settings; 10397 return NULL; 10398 } 10399 10400 ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) 10401 { 10402 if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name))) 10403 return settings; 10404 return CreateNewWindowSettings(name); 10405 } 10406 10407 ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) 10408 { 10409 ImGuiContext& g = *GImGui; 10410 const ImGuiID type_hash = ImHashStr(type_name); 10411 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10412 if (g.SettingsHandlers[handler_n].TypeHash == type_hash) 10413 return &g.SettingsHandlers[handler_n]; 10414 return NULL; 10415 } 10416 10417 void ImGui::ClearIniSettings() 10418 { 10419 ImGuiContext& g = *GImGui; 10420 g.SettingsIniData.clear(); 10421 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10422 if (g.SettingsHandlers[handler_n].ClearAllFn) 10423 g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]); 10424 } 10425 10426 void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) 10427 { 10428 size_t file_data_size = 0; 10429 char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); 10430 if (!file_data) 10431 return; 10432 LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); 10433 IM_FREE(file_data); 10434 } 10435 10436 // Zero-tolerance, no error reporting, cheap .ini parsing 10437 void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) 10438 { 10439 ImGuiContext& g = *GImGui; 10440 IM_ASSERT(g.Initialized); 10441 //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()"); 10442 //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); 10443 10444 // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). 10445 // 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.. 10446 if (ini_size == 0) 10447 ini_size = strlen(ini_data); 10448 g.SettingsIniData.Buf.resize((int)ini_size + 1); 10449 char* const buf = g.SettingsIniData.Buf.Data; 10450 char* const buf_end = buf + ini_size; 10451 memcpy(buf, ini_data, ini_size); 10452 buf_end[0] = 0; 10453 10454 // Call pre-read handlers 10455 // Some types will clear their data (e.g. dock information) some types will allow merge/override (window) 10456 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10457 if (g.SettingsHandlers[handler_n].ReadInitFn) 10458 g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]); 10459 10460 void* entry_data = NULL; 10461 ImGuiSettingsHandler* entry_handler = NULL; 10462 10463 char* line_end = NULL; 10464 for (char* line = buf; line < buf_end; line = line_end + 1) 10465 { 10466 // Skip new lines markers, then find end of the line 10467 while (*line == '\n' || *line == '\r') 10468 line++; 10469 line_end = line; 10470 while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') 10471 line_end++; 10472 line_end[0] = 0; 10473 if (line[0] == ';') 10474 continue; 10475 if (line[0] == '[' && line_end > line && line_end[-1] == ']') 10476 { 10477 // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. 10478 line_end[-1] = 0; 10479 const char* name_end = line_end - 1; 10480 const char* type_start = line + 1; 10481 char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']'); 10482 const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; 10483 if (!type_end || !name_start) 10484 continue; 10485 *type_end = 0; // Overwrite first ']' 10486 name_start++; // Skip second '[' 10487 entry_handler = FindSettingsHandler(type_start); 10488 entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; 10489 } 10490 else if (entry_handler != NULL && entry_data != NULL) 10491 { 10492 // Let type handler parse the line 10493 entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); 10494 } 10495 } 10496 g.SettingsLoaded = true; 10497 10498 // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary) 10499 memcpy(buf, ini_data, ini_size); 10500 10501 // Call post-read handlers 10502 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10503 if (g.SettingsHandlers[handler_n].ApplyAllFn) 10504 g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]); 10505 } 10506 10507 void ImGui::SaveIniSettingsToDisk(const char* ini_filename) 10508 { 10509 ImGuiContext& g = *GImGui; 10510 g.SettingsDirtyTimer = 0.0f; 10511 if (!ini_filename) 10512 return; 10513 10514 size_t ini_data_size = 0; 10515 const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); 10516 ImFileHandle f = ImFileOpen(ini_filename, "wt"); 10517 if (!f) 10518 return; 10519 ImFileWrite(ini_data, sizeof(char), ini_data_size, f); 10520 ImFileClose(f); 10521 } 10522 10523 // Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer 10524 const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) 10525 { 10526 ImGuiContext& g = *GImGui; 10527 g.SettingsDirtyTimer = 0.0f; 10528 g.SettingsIniData.Buf.resize(0); 10529 g.SettingsIniData.Buf.push_back(0); 10530 for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) 10531 { 10532 ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; 10533 handler->WriteAllFn(&g, handler, &g.SettingsIniData); 10534 } 10535 if (out_size) 10536 *out_size = (size_t)g.SettingsIniData.size(); 10537 return g.SettingsIniData.c_str(); 10538 } 10539 10540 static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) 10541 { 10542 ImGuiContext& g = *ctx; 10543 for (int i = 0; i != g.Windows.Size; i++) 10544 g.Windows[i]->SettingsOffset = -1; 10545 g.SettingsWindows.clear(); 10546 } 10547 10548 static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) 10549 { 10550 ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name); 10551 ImGuiID id = settings->ID; 10552 *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry 10553 settings->ID = id; 10554 settings->WantApply = true; 10555 return (void*)settings; 10556 } 10557 10558 static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) 10559 { 10560 ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; 10561 int x, y; 10562 int i; 10563 if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } 10564 else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } 10565 else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } 10566 } 10567 10568 // Apply to existing windows (if any) 10569 static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) 10570 { 10571 ImGuiContext& g = *ctx; 10572 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 10573 if (settings->WantApply) 10574 { 10575 if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID)) 10576 ApplyWindowSettings(window, settings); 10577 settings->WantApply = false; 10578 } 10579 } 10580 10581 static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) 10582 { 10583 // Gather data from windows that were active during this session 10584 // (if a window wasn't opened in this session we preserve its settings) 10585 ImGuiContext& g = *ctx; 10586 for (int i = 0; i != g.Windows.Size; i++) 10587 { 10588 ImGuiWindow* window = g.Windows[i]; 10589 if (window->Flags & ImGuiWindowFlags_NoSavedSettings) 10590 continue; 10591 10592 ImGuiWindowSettings* settings = (window->SettingsOffset != -1) ? g.SettingsWindows.ptr_from_offset(window->SettingsOffset) : ImGui::FindWindowSettings(window->ID); 10593 if (!settings) 10594 { 10595 settings = ImGui::CreateNewWindowSettings(window->Name); 10596 window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); 10597 } 10598 IM_ASSERT(settings->ID == window->ID); 10599 settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); 10600 settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); 10601 settings->Collapsed = window->Collapsed; 10602 } 10603 10604 // Write to text buffer 10605 buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve 10606 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 10607 { 10608 const char* settings_name = settings->GetName(); 10609 buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); 10610 buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); 10611 buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); 10612 buf->appendf("Collapsed=%d\n", settings->Collapsed); 10613 buf->append("\n"); 10614 } 10615 } 10616 10617 10618 //----------------------------------------------------------------------------- 10619 // [SECTION] VIEWPORTS, PLATFORM WINDOWS 10620 //----------------------------------------------------------------------------- 10621 // - GetMainViewport() 10622 // - UpdateViewportsNewFrame() [Internal] 10623 // (this section is more complete in the 'docking' branch) 10624 //----------------------------------------------------------------------------- 10625 10626 ImGuiViewport* ImGui::GetMainViewport() 10627 { 10628 ImGuiContext& g = *GImGui; 10629 return g.Viewports[0]; 10630 } 10631 10632 // Update viewports and monitor infos 10633 static void ImGui::UpdateViewportsNewFrame() 10634 { 10635 ImGuiContext& g = *GImGui; 10636 IM_ASSERT(g.Viewports.Size == 1); 10637 10638 // Update main viewport with current platform position. 10639 // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent. 10640 ImGuiViewportP* main_viewport = g.Viewports[0]; 10641 main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp; 10642 main_viewport->Pos = ImVec2(0.0f, 0.0f); 10643 main_viewport->Size = g.IO.DisplaySize; 10644 10645 for (int n = 0; n < g.Viewports.Size; n++) 10646 { 10647 ImGuiViewportP* viewport = g.Viewports[n]; 10648 10649 // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. 10650 viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; 10651 viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; 10652 viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f); 10653 viewport->UpdateWorkRect(); 10654 } 10655 } 10656 10657 //----------------------------------------------------------------------------- 10658 // [SECTION] DOCKING 10659 //----------------------------------------------------------------------------- 10660 10661 // (this section is filled in the 'docking' branch) 10662 10663 10664 //----------------------------------------------------------------------------- 10665 // [SECTION] PLATFORM DEPENDENT HELPERS 10666 //----------------------------------------------------------------------------- 10667 10668 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) 10669 10670 #ifdef _MSC_VER 10671 #pragma comment(lib, "user32") 10672 #pragma comment(lib, "kernel32") 10673 #endif 10674 10675 // Win32 clipboard implementation 10676 // We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() 10677 static const char* GetClipboardTextFn_DefaultImpl(void*) 10678 { 10679 ImGuiContext& g = *GImGui; 10680 g.ClipboardHandlerData.clear(); 10681 if (!::OpenClipboard(NULL)) 10682 return NULL; 10683 HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); 10684 if (wbuf_handle == NULL) 10685 { 10686 ::CloseClipboard(); 10687 return NULL; 10688 } 10689 if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) 10690 { 10691 int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); 10692 g.ClipboardHandlerData.resize(buf_len); 10693 ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); 10694 } 10695 ::GlobalUnlock(wbuf_handle); 10696 ::CloseClipboard(); 10697 return g.ClipboardHandlerData.Data; 10698 } 10699 10700 static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 10701 { 10702 if (!::OpenClipboard(NULL)) 10703 return; 10704 const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); 10705 HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); 10706 if (wbuf_handle == NULL) 10707 { 10708 ::CloseClipboard(); 10709 return; 10710 } 10711 WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); 10712 ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); 10713 ::GlobalUnlock(wbuf_handle); 10714 ::EmptyClipboard(); 10715 if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) 10716 ::GlobalFree(wbuf_handle); 10717 ::CloseClipboard(); 10718 } 10719 10720 #elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) 10721 10722 #include <Carbon/Carbon.h> // Use old API to avoid need for separate .mm file 10723 static PasteboardRef main_clipboard = 0; 10724 10725 // OSX clipboard implementation 10726 // If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! 10727 static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 10728 { 10729 if (!main_clipboard) 10730 PasteboardCreate(kPasteboardClipboard, &main_clipboard); 10731 PasteboardClear(main_clipboard); 10732 CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); 10733 if (cf_data) 10734 { 10735 PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); 10736 CFRelease(cf_data); 10737 } 10738 } 10739 10740 static const char* GetClipboardTextFn_DefaultImpl(void*) 10741 { 10742 if (!main_clipboard) 10743 PasteboardCreate(kPasteboardClipboard, &main_clipboard); 10744 PasteboardSynchronize(main_clipboard); 10745 10746 ItemCount item_count = 0; 10747 PasteboardGetItemCount(main_clipboard, &item_count); 10748 for (ItemCount i = 0; i < item_count; i++) 10749 { 10750 PasteboardItemID item_id = 0; 10751 PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); 10752 CFArrayRef flavor_type_array = 0; 10753 PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); 10754 for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) 10755 { 10756 CFDataRef cf_data; 10757 if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) 10758 { 10759 ImGuiContext& g = *GImGui; 10760 g.ClipboardHandlerData.clear(); 10761 int length = (int)CFDataGetLength(cf_data); 10762 g.ClipboardHandlerData.resize(length + 1); 10763 CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); 10764 g.ClipboardHandlerData[length] = 0; 10765 CFRelease(cf_data); 10766 return g.ClipboardHandlerData.Data; 10767 } 10768 } 10769 } 10770 return NULL; 10771 } 10772 10773 #else 10774 10775 // Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. 10776 static const char* GetClipboardTextFn_DefaultImpl(void*) 10777 { 10778 ImGuiContext& g = *GImGui; 10779 return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); 10780 } 10781 10782 static void SetClipboardTextFn_DefaultImpl(void*, const char* text) 10783 { 10784 ImGuiContext& g = *GImGui; 10785 g.ClipboardHandlerData.clear(); 10786 const char* text_end = text + strlen(text); 10787 g.ClipboardHandlerData.resize((int)(text_end - text) + 1); 10788 memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); 10789 g.ClipboardHandlerData[(int)(text_end - text)] = 0; 10790 } 10791 10792 #endif 10793 10794 // Win32 API IME support (for Asian languages, etc.) 10795 #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) 10796 10797 #include <imm.h> 10798 #ifdef _MSC_VER 10799 #pragma comment(lib, "imm32") 10800 #endif 10801 10802 static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) 10803 { 10804 // Notify OS Input Method Editor of text input position 10805 ImGuiIO& io = ImGui::GetIO(); 10806 if (HWND hwnd = (HWND)io.ImeWindowHandle) 10807 if (HIMC himc = ::ImmGetContext(hwnd)) 10808 { 10809 COMPOSITIONFORM cf; 10810 cf.ptCurrentPos.x = x; 10811 cf.ptCurrentPos.y = y; 10812 cf.dwStyle = CFS_FORCE_POSITION; 10813 ::ImmSetCompositionWindow(himc, &cf); 10814 ::ImmReleaseContext(hwnd, himc); 10815 } 10816 } 10817 10818 #else 10819 10820 static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} 10821 10822 #endif 10823 10824 //----------------------------------------------------------------------------- 10825 // [SECTION] METRICS/DEBUGGER WINDOW 10826 //----------------------------------------------------------------------------- 10827 // - RenderViewportThumbnail() [Internal] 10828 // - RenderViewportsThumbnails() [Internal] 10829 // - MetricsHelpMarker() [Internal] 10830 // - ShowMetricsWindow() 10831 // - DebugNodeColumns() [Internal] 10832 // - DebugNodeDrawList() [Internal] 10833 // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] 10834 // - DebugNodeStorage() [Internal] 10835 // - DebugNodeTabBar() [Internal] 10836 // - DebugNodeViewport() [Internal] 10837 // - DebugNodeWindow() [Internal] 10838 // - DebugNodeWindowSettings() [Internal] 10839 // - DebugNodeWindowsList() [Internal] 10840 //----------------------------------------------------------------------------- 10841 10842 #ifndef IMGUI_DISABLE_METRICS_WINDOW 10843 10844 void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) 10845 { 10846 ImGuiContext& g = *GImGui; 10847 ImGuiWindow* window = g.CurrentWindow; 10848 10849 ImVec2 scale = bb.GetSize() / viewport->Size; 10850 ImVec2 off = bb.Min - viewport->Pos * scale; 10851 float alpha_mul = 1.0f; 10852 window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); 10853 for (int i = 0; i != g.Windows.Size; i++) 10854 { 10855 ImGuiWindow* thumb_window = g.Windows[i]; 10856 if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) 10857 continue; 10858 10859 ImRect thumb_r = thumb_window->Rect(); 10860 ImRect title_r = thumb_window->TitleBarRect(); 10861 thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); 10862 title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height 10863 thumb_r.ClipWithFull(bb); 10864 title_r.ClipWithFull(bb); 10865 const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); 10866 window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul)); 10867 window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul)); 10868 window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); 10869 window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name)); 10870 } 10871 draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); 10872 } 10873 10874 static void RenderViewportsThumbnails() 10875 { 10876 ImGuiContext& g = *GImGui; 10877 ImGuiWindow* window = g.CurrentWindow; 10878 10879 // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. 10880 float SCALE = 1.0f / 8.0f; 10881 ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); 10882 for (int n = 0; n < g.Viewports.Size; n++) 10883 bb_full.Add(g.Viewports[n]->GetMainRect()); 10884 ImVec2 p = window->DC.CursorPos; 10885 ImVec2 off = p - bb_full.Min * SCALE; 10886 for (int n = 0; n < g.Viewports.Size; n++) 10887 { 10888 ImGuiViewportP* viewport = g.Viewports[n]; 10889 ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); 10890 ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); 10891 } 10892 ImGui::Dummy(bb_full.GetSize() * SCALE); 10893 } 10894 10895 // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. 10896 static void MetricsHelpMarker(const char* desc) 10897 { 10898 ImGui::TextDisabled("(?)"); 10899 if (ImGui::IsItemHovered()) 10900 { 10901 ImGui::BeginTooltip(); 10902 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); 10903 ImGui::TextUnformatted(desc); 10904 ImGui::PopTextWrapPos(); 10905 ImGui::EndTooltip(); 10906 } 10907 } 10908 10909 #ifndef IMGUI_DISABLE_DEMO_WINDOWS 10910 namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } 10911 #endif 10912 10913 void ImGui::ShowMetricsWindow(bool* p_open) 10914 { 10915 if (!Begin("Dear ImGui Metrics/Debugger", p_open)) 10916 { 10917 End(); 10918 return; 10919 } 10920 10921 ImGuiContext& g = *GImGui; 10922 ImGuiIO& io = g.IO; 10923 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; 10924 10925 // Basic info 10926 Text("Dear ImGui %s", GetVersion()); 10927 Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); 10928 Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); 10929 Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); 10930 Text("%d active allocations", io.MetricsActiveAllocations); 10931 //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } 10932 10933 Separator(); 10934 10935 // Debugging enums 10936 enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type 10937 const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" }; 10938 enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type 10939 const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" }; 10940 if (cfg->ShowWindowsRectsType < 0) 10941 cfg->ShowWindowsRectsType = WRT_WorkRect; 10942 if (cfg->ShowTablesRectsType < 0) 10943 cfg->ShowTablesRectsType = TRT_WorkRect; 10944 10945 struct Funcs 10946 { 10947 static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n) 10948 { 10949 if (rect_type == TRT_OuterRect) { return table->OuterRect; } 10950 else if (rect_type == TRT_InnerRect) { return table->InnerRect; } 10951 else if (rect_type == TRT_WorkRect) { return table->WorkRect; } 10952 else if (rect_type == TRT_HostClipRect) { return table->HostClipRect; } 10953 else if (rect_type == TRT_InnerClipRect) { return table->InnerClipRect; } 10954 else if (rect_type == TRT_BackgroundClipRect) { return table->BgClipRect; } 10955 else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table->LastOuterHeight); } 10956 else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); } 10957 else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } 10958 else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } // Note: y1/y2 not always accurate 10959 else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } 10960 else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table->LastFirstRowHeight); } 10961 else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } 10962 IM_ASSERT(0); 10963 return ImRect(); 10964 } 10965 10966 static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) 10967 { 10968 if (rect_type == WRT_OuterRect) { return window->Rect(); } 10969 else if (rect_type == WRT_OuterRectClipped) { return window->OuterRectClipped; } 10970 else if (rect_type == WRT_InnerRect) { return window->InnerRect; } 10971 else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } 10972 else if (rect_type == WRT_WorkRect) { return window->WorkRect; } 10973 else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } 10974 else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); } 10975 else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } 10976 IM_ASSERT(0); 10977 return ImRect(); 10978 } 10979 }; 10980 10981 // Tools 10982 if (TreeNode("Tools")) 10983 { 10984 // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. 10985 if (Button("Item Picker..")) 10986 DebugStartItemPicker(); 10987 SameLine(); 10988 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."); 10989 10990 Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); 10991 Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); 10992 SameLine(); 10993 SetNextItemWidth(GetFontSize() * 12); 10994 cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count); 10995 if (cfg->ShowWindowsRects && g.NavWindow != NULL) 10996 { 10997 BulletText("'%s':", g.NavWindow->Name); 10998 Indent(); 10999 for (int rect_n = 0; rect_n < WRT_Count; rect_n++) 11000 { 11001 ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n); 11002 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]); 11003 } 11004 Unindent(); 11005 } 11006 Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); 11007 Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); 11008 11009 Checkbox("Show tables rectangles", &cfg->ShowTablesRects); 11010 SameLine(); 11011 SetNextItemWidth(GetFontSize() * 12); 11012 cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count); 11013 if (cfg->ShowTablesRects && g.NavWindow != NULL) 11014 { 11015 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) 11016 { 11017 ImGuiTable* table = g.Tables.GetByIndex(table_n); 11018 if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) 11019 continue; 11020 11021 BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); 11022 if (IsItemHovered()) 11023 GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); 11024 Indent(); 11025 char buf[128]; 11026 for (int rect_n = 0; rect_n < TRT_Count; rect_n++) 11027 { 11028 if (rect_n >= TRT_ColumnsRect) 11029 { 11030 if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect) 11031 continue; 11032 for (int column_n = 0; column_n < table->ColumnsCount; column_n++) 11033 { 11034 ImRect r = Funcs::GetTableRect(table, rect_n, column_n); 11035 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); 11036 Selectable(buf); 11037 if (IsItemHovered()) 11038 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); 11039 } 11040 } 11041 else 11042 { 11043 ImRect r = Funcs::GetTableRect(table, rect_n, -1); 11044 ImFormatString(buf, IM_ARRAYSIZE(buf), "(%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(), trt_rects_names[rect_n]); 11045 Selectable(buf); 11046 if (IsItemHovered()) 11047 GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); 11048 } 11049 } 11050 Unindent(); 11051 } 11052 } 11053 11054 TreePop(); 11055 } 11056 11057 // Windows 11058 DebugNodeWindowsList(&g.Windows, "Windows"); 11059 //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder"); 11060 11061 // DrawLists 11062 int drawlist_count = 0; 11063 for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) 11064 drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); 11065 if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) 11066 { 11067 for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) 11068 { 11069 ImGuiViewportP* viewport = g.Viewports[viewport_i]; 11070 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) 11071 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) 11072 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); 11073 } 11074 TreePop(); 11075 } 11076 11077 // Viewports 11078 if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) 11079 { 11080 Indent(GetTreeNodeToLabelSpacing()); 11081 RenderViewportsThumbnails(); 11082 Unindent(GetTreeNodeToLabelSpacing()); 11083 for (int i = 0; i < g.Viewports.Size; i++) 11084 DebugNodeViewport(g.Viewports[i]); 11085 TreePop(); 11086 } 11087 11088 // Details for Popups 11089 if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) 11090 { 11091 for (int i = 0; i < g.OpenPopupStack.Size; i++) 11092 { 11093 ImGuiWindow* window = g.OpenPopupStack[i].Window; 11094 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" : ""); 11095 } 11096 TreePop(); 11097 } 11098 11099 // Details for TabBars 11100 if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) 11101 { 11102 for (int n = 0; n < g.TabBars.GetSize(); n++) 11103 DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar"); 11104 TreePop(); 11105 } 11106 11107 // Details for Tables 11108 if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) 11109 { 11110 for (int n = 0; n < g.Tables.GetSize(); n++) 11111 DebugNodeTable(g.Tables.GetByIndex(n)); 11112 TreePop(); 11113 } 11114 11115 // Details for Fonts 11116 #ifndef IMGUI_DISABLE_DEMO_WINDOWS 11117 ImFontAtlas* atlas = g.IO.Fonts; 11118 if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) 11119 { 11120 ShowFontAtlas(atlas); 11121 TreePop(); 11122 } 11123 #endif 11124 11125 // Details for Docking 11126 #ifdef IMGUI_HAS_DOCK 11127 if (TreeNode("Docking")) 11128 { 11129 TreePop(); 11130 } 11131 #endif // #ifdef IMGUI_HAS_DOCK 11132 11133 // Settings 11134 if (TreeNode("Settings")) 11135 { 11136 if (SmallButton("Clear")) 11137 ClearIniSettings(); 11138 SameLine(); 11139 if (SmallButton("Save to memory")) 11140 SaveIniSettingsToMemory(); 11141 SameLine(); 11142 if (SmallButton("Save to disk")) 11143 SaveIniSettingsToDisk(g.IO.IniFilename); 11144 SameLine(); 11145 if (g.IO.IniFilename) 11146 Text("\"%s\"", g.IO.IniFilename); 11147 else 11148 TextUnformatted("<NULL>"); 11149 Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); 11150 if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) 11151 { 11152 for (int n = 0; n < g.SettingsHandlers.Size; n++) 11153 BulletText("%s", g.SettingsHandlers[n].TypeName); 11154 TreePop(); 11155 } 11156 if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) 11157 { 11158 for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) 11159 DebugNodeWindowSettings(settings); 11160 TreePop(); 11161 } 11162 11163 if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) 11164 { 11165 for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) 11166 DebugNodeTableSettings(settings); 11167 TreePop(); 11168 } 11169 11170 #ifdef IMGUI_HAS_DOCK 11171 #endif // #ifdef IMGUI_HAS_DOCK 11172 11173 if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) 11174 { 11175 InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly); 11176 TreePop(); 11177 } 11178 TreePop(); 11179 } 11180 11181 // Misc Details 11182 if (TreeNode("Internal state")) 11183 { 11184 const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); 11185 11186 Text("WINDOWING"); 11187 Indent(); 11188 Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); 11189 Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL"); 11190 Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); 11191 Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); 11192 Unindent(); 11193 11194 Text("ITEMS"); 11195 Indent(); 11196 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]); 11197 Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); 11198 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 11199 Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); 11200 Unindent(); 11201 11202 Text("NAV,FOCUS"); 11203 Indent(); 11204 Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); 11205 Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); 11206 Text("NavInputSource: %s", input_source_names[g.NavInputSource]); 11207 Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); 11208 Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); 11209 Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); 11210 Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); 11211 Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); 11212 Unindent(); 11213 11214 TreePop(); 11215 } 11216 11217 // Overlay: Display windows Rectangles and Begin Order 11218 if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder) 11219 { 11220 for (int n = 0; n < g.Windows.Size; n++) 11221 { 11222 ImGuiWindow* window = g.Windows[n]; 11223 if (!window->WasActive) 11224 continue; 11225 ImDrawList* draw_list = GetForegroundDrawList(window); 11226 if (cfg->ShowWindowsRects) 11227 { 11228 ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType); 11229 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); 11230 } 11231 if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow)) 11232 { 11233 char buf[32]; 11234 ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); 11235 float font_size = GetFontSize(); 11236 draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); 11237 draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf); 11238 } 11239 } 11240 } 11241 11242 // Overlay: Display Tables Rectangles 11243 if (cfg->ShowTablesRects) 11244 { 11245 for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) 11246 { 11247 ImGuiTable* table = g.Tables.GetByIndex(table_n); 11248 if (table->LastFrameActive < g.FrameCount - 1) 11249 continue; 11250 ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow); 11251 if (cfg->ShowTablesRectsType >= TRT_ColumnsRect) 11252 { 11253 for (int column_n = 0; column_n < table->ColumnsCount; column_n++) 11254 { 11255 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n); 11256 ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255); 11257 float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f; 11258 draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness); 11259 } 11260 } 11261 else 11262 { 11263 ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1); 11264 draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255)); 11265 } 11266 } 11267 } 11268 11269 #ifdef IMGUI_HAS_DOCK 11270 // Overlay: Display Docking info 11271 if (show_docking_nodes && g.IO.KeyCtrl) 11272 { 11273 } 11274 #endif // #ifdef IMGUI_HAS_DOCK 11275 11276 End(); 11277 } 11278 11279 // [DEBUG] Display contents of Columns 11280 void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) 11281 { 11282 if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) 11283 return; 11284 BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); 11285 for (int column_n = 0; column_n < columns->Columns.Size; column_n++) 11286 BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm)); 11287 TreePop(); 11288 } 11289 11290 // [DEBUG] Display contents of ImDrawList 11291 void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label) 11292 { 11293 ImGuiContext& g = *GImGui; 11294 ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; 11295 int cmd_count = draw_list->CmdBuffer.Size; 11296 if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL) 11297 cmd_count--; 11298 bool node_open = 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, cmd_count); 11299 if (draw_list == GetWindowDrawList()) 11300 { 11301 SameLine(); 11302 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) 11303 if (node_open) 11304 TreePop(); 11305 return; 11306 } 11307 11308 ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list 11309 if (window && IsItemHovered()) 11310 fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); 11311 if (!node_open) 11312 return; 11313 11314 if (window && !window->WasActive) 11315 TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!"); 11316 11317 for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++) 11318 { 11319 if (pcmd->UserCallback) 11320 { 11321 BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); 11322 continue; 11323 } 11324 11325 char buf[300]; 11326 ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", 11327 pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId, 11328 pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); 11329 bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); 11330 if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) 11331 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes); 11332 if (!pcmd_node_open) 11333 continue; 11334 11335 // Calculate approximate coverage area (touched pixel count) 11336 // This will be in pixels squared as long there's no post-scaling happening to the renderer output. 11337 const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 11338 const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset; 11339 float total_area = 0.0f; 11340 for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; ) 11341 { 11342 ImVec2 triangle[3]; 11343 for (int n = 0; n < 3; n++, idx_n++) 11344 triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos; 11345 total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]); 11346 } 11347 11348 // Display vertex information summary. Hover to get all triangles drawn in wire-frame 11349 ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); 11350 Selectable(buf); 11351 if (IsItemHovered() && fg_draw_list) 11352 DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false); 11353 11354 // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. 11355 ImGuiListClipper clipper; 11356 clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. 11357 while (clipper.Step()) 11358 for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++) 11359 { 11360 char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf); 11361 ImVec2 triangle[3]; 11362 for (int n = 0; n < 3; n++, idx_i++) 11363 { 11364 const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i]; 11365 triangle[n] = v.pos; 11366 buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", 11367 (n == 0) ? "Vert:" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); 11368 } 11369 11370 Selectable(buf, false); 11371 if (fg_draw_list && IsItemHovered()) 11372 { 11373 ImDrawListFlags backup_flags = fg_draw_list->Flags; 11374 fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. 11375 fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); 11376 fg_draw_list->Flags = backup_flags; 11377 } 11378 } 11379 TreePop(); 11380 } 11381 TreePop(); 11382 } 11383 11384 // [DEBUG] Display mesh/aabb of a ImDrawCmd 11385 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) 11386 { 11387 IM_ASSERT(show_mesh || show_aabb); 11388 ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; 11389 ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; 11390 11391 // Draw wire-frame version of all triangles 11392 ImRect clip_rect = draw_cmd->ClipRect; 11393 ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); 11394 ImDrawListFlags backup_flags = out_draw_list->Flags; 11395 out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. 11396 for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; ) 11397 { 11398 ImVec2 triangle[3]; 11399 for (int n = 0; n < 3; n++, idx_n++) 11400 vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); 11401 if (show_mesh) 11402 out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles 11403 } 11404 // Draw bounding boxes 11405 if (show_aabb) 11406 { 11407 out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU 11408 out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles 11409 } 11410 out_draw_list->Flags = backup_flags; 11411 } 11412 11413 // [DEBUG] Display contents of ImGuiStorage 11414 void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) 11415 { 11416 if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes())) 11417 return; 11418 for (int n = 0; n < storage->Data.Size; n++) 11419 { 11420 const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n]; 11421 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. 11422 } 11423 TreePop(); 11424 } 11425 11426 // [DEBUG] Display contents of ImGuiTabBar 11427 void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) 11428 { 11429 // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. 11430 char buf[256]; 11431 char* p = buf; 11432 const char* buf_end = buf + IM_ARRAYSIZE(buf); 11433 const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); 11434 p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); 11435 IM_UNUSED(p); 11436 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } 11437 bool open = TreeNode(tab_bar, "%s", buf); 11438 if (!is_active) { PopStyleColor(); } 11439 if (is_active && IsItemHovered()) 11440 { 11441 ImDrawList* draw_list = GetForegroundDrawList(); 11442 draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255)); 11443 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); 11444 draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); 11445 } 11446 if (open) 11447 { 11448 for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) 11449 { 11450 const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; 11451 PushID(tab); 11452 if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); 11453 if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); 11454 Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", 11455 tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth); 11456 PopID(); 11457 } 11458 TreePop(); 11459 } 11460 } 11461 11462 void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) 11463 { 11464 SetNextItemOpen(true, ImGuiCond_Once); 11465 if (TreeNode("viewport0", "Viewport #%d", 0)) 11466 { 11467 ImGuiWindowFlags flags = viewport->Flags; 11468 BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", 11469 viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, 11470 viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y); 11471 BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags, 11472 (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", 11473 (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", 11474 (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : ""); 11475 for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) 11476 for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) 11477 DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); 11478 TreePop(); 11479 } 11480 } 11481 11482 void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) 11483 { 11484 if (window == NULL) 11485 { 11486 BulletText("%s: NULL", label); 11487 return; 11488 } 11489 11490 ImGuiContext& g = *GImGui; 11491 const bool is_active = window->WasActive; 11492 ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; 11493 if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } 11494 const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*"); 11495 if (!is_active) { PopStyleColor(); } 11496 if (IsItemHovered() && is_active) 11497 GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); 11498 if (!open) 11499 return; 11500 11501 if (window->MemoryCompacted) 11502 TextDisabled("Note: some memory buffers have been compacted/freed."); 11503 11504 ImGuiWindowFlags flags = window->Flags; 11505 DebugNodeDrawList(window, window->DrawList, "DrawList"); 11506 BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y); 11507 BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, 11508 (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", 11509 (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", 11510 (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); 11511 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" : ""); 11512 BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); 11513 BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); 11514 for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) 11515 { 11516 ImRect r = window->NavRectRel[layer]; 11517 if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) 11518 { 11519 BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); 11520 continue; 11521 } 11522 BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); 11523 if (IsItemHovered()) 11524 GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); 11525 } 11526 BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); 11527 if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } 11528 if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } 11529 if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } 11530 if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) 11531 { 11532 for (int n = 0; n < window->ColumnsStorage.Size; n++) 11533 DebugNodeColumns(&window->ColumnsStorage[n]); 11534 TreePop(); 11535 } 11536 DebugNodeStorage(&window->StateStorage, "Storage"); 11537 TreePop(); 11538 } 11539 11540 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings) 11541 { 11542 Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d", 11543 settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); 11544 } 11545 11546 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label) 11547 { 11548 if (!TreeNode(label, "%s (%d)", label, windows->Size)) 11549 return; 11550 Text("(In front-to-back order:)"); 11551 for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back 11552 { 11553 PushID((*windows)[i]); 11554 DebugNodeWindow((*windows)[i], "Window"); 11555 PopID(); 11556 } 11557 TreePop(); 11558 } 11559 11560 #else 11561 11562 void ImGui::ShowMetricsWindow(bool*) {} 11563 void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} 11564 void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {} 11565 void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} 11566 void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} 11567 void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {} 11568 void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {} 11569 void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} 11570 void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {} 11571 void ImGui::DebugNodeViewport(ImGuiViewportP*) {} 11572 11573 #endif 11574 11575 //----------------------------------------------------------------------------- 11576 11577 // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. 11578 // 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. 11579 #ifdef IMGUI_INCLUDE_IMGUI_USER_INL 11580 #include "imgui_user.inl" 11581 #endif 11582 11583 //----------------------------------------------------------------------------- 11584 11585 #endif // #ifndef IMGUI_DISABLE